From ef69d95583dcfd6038d33832b10740dafdecf738 Mon Sep 17 00:00:00 2001 From: Daniel Ponte Date: Sun, 17 Nov 2024 21:46:10 -0500 Subject: [PATCH 1/5] Initial calls normalization fix wip --- .mockery.yaml | 2 +- cmd/stillbox/main.go | 5 +- pkg/alerting/alert/alert.go | 5 +- pkg/alerting/stats.go | 3 +- pkg/calls/call.go | 17 + pkg/database/calls.sql.go | 19 +- pkg/database/database.go | 39 +- pkg/database/extend.go | 8 +- pkg/database/mocks/DB.go | 1631 --------------- pkg/database/mocks/Store.go | 1786 +++++++++++++++++ pkg/database/models.go | 26 +- pkg/database/querier.go | 8 +- pkg/database/talkgroups.go | 35 +- pkg/database/talkgroups.sql.go | 143 +- pkg/nexus/commands.go | 2 +- pkg/server/server.go | 4 +- pkg/sinks/database.go | 48 +- pkg/talkgroups/cache.go | 2 +- pkg/talkgroups/importer/import.go | 5 +- sql/postgres/migrations/001_initial.up.sql | 24 +- .../migrations/002_tglearned.down.sql | 25 + sql/postgres/migrations/002_tglearned.up.sql | 24 + sql/postgres/migrations/initial.sql | 121 ++ sql/postgres/queries/calls.sql | 3 +- sql/postgres/queries/talkgroups.sql | 61 +- sql/sqlc.yaml | 1 + 26 files changed, 2250 insertions(+), 1797 deletions(-) delete mode 100644 pkg/database/mocks/DB.go create mode 100644 pkg/database/mocks/Store.go create mode 100644 sql/postgres/migrations/002_tglearned.down.sql create mode 100644 sql/postgres/migrations/002_tglearned.up.sql create mode 100644 sql/postgres/migrations/initial.sql diff --git a/.mockery.yaml b/.mockery.yaml index ab4e084..fa78e2c 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -7,4 +7,4 @@ packages: dynatron.me/x/stillbox/pkg/database: config: interfaces: - DB: + Store: diff --git a/cmd/stillbox/main.go b/cmd/stillbox/main.go index 95e8284..29703b1 100644 --- a/cmd/stillbox/main.go +++ b/cmd/stillbox/main.go @@ -35,8 +35,5 @@ func main() { cmds := append([]*cobra.Command{serve.Command(cfg)}, admin.Command(cfg)...) rootCmd.AddCommand(cmds...) - err := rootCmd.Execute() - if err != nil { - log.Fatal().Err(err).Msg("Dying") - } + rootCmd.Execute() } diff --git a/pkg/alerting/alert/alert.go b/pkg/alerting/alert/alert.go index de64278..3889a61 100644 --- a/pkg/alerting/alert/alert.go +++ b/pkg/alerting/alert/alert.go @@ -10,12 +10,11 @@ import ( "dynatron.me/x/stillbox/pkg/database" "dynatron.me/x/stillbox/pkg/talkgroups" - "github.com/google/uuid" "github.com/jackc/pgx/v5/pgtype" ) type Alert struct { - ID uuid.UUID + ID int Timestamp time.Time TGName string Score trending.Score[talkgroups.ID] @@ -34,7 +33,6 @@ func (a *Alert) ToAddAlertParams() database.AddAlertParams { } return database.AddAlertParams{ - ID: a.ID, Time: pgtype.Timestamptz{Time: a.Timestamp, Valid: true}, SystemID: int(a.Score.ID.System), TGID: int(a.Score.ID.Talkgroup), @@ -48,7 +46,6 @@ func (a *Alert) ToAddAlertParams() database.AddAlertParams { // 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) { d := Alert{ - ID: uuid.New(), Score: score, Timestamp: time.Now(), Weight: 1.0, diff --git a/pkg/alerting/stats.go b/pkg/alerting/stats.go index 2daa497..642a8b3 100644 --- a/pkg/alerting/stats.go +++ b/pkg/alerting/stats.go @@ -40,7 +40,8 @@ func (as *alerter) tgStatsHandler(w http.ResponseWriter, r *http.Request) { ctx := r.Context() db := database.FromCtx(ctx) - tgs, err := db.GetTalkgroupsWithLearnedBySysTGID(ctx, as.scoredTGsTuple()) + tgt := as.scoredTGsTuple() + tgs, err := db.GetTalkgroupsWithLearnedBySysTGID(ctx, tgt) if err != nil { log.Error().Err(err).Msg("stats TG get failed") http.Error(w, err.Error(), http.StatusInternalServerError) diff --git a/pkg/calls/call.go b/pkg/calls/call.go index 6359940..d76b530 100644 --- a/pkg/calls/call.go +++ b/pkg/calls/call.go @@ -1,11 +1,13 @@ package calls import ( + "context" "fmt" "time" "dynatron.me/x/stillbox/internal/audio" "dynatron.me/x/stillbox/pkg/auth" + "dynatron.me/x/stillbox/pkg/database" "dynatron.me/x/stillbox/pkg/pb" "dynatron.me/x/stillbox/pkg/talkgroups" @@ -111,6 +113,21 @@ func (c *Call) ToPB() *pb.Call { } } +func (c *Call) LearnTG(ctx context.Context, db database.Store) (learnedId int, err error) { + err = db.AddTalkgroupWithLearnedFlag(ctx, int32(c.System), int32(c.Talkgroup)) + if err != nil { + return 0, fmt.Errorf("addTalkgroupWithLearnedFlag: %w", err) + } + + return db.AddLearnedTalkgroup(ctx, database.AddLearnedTalkgroupParams{ + SystemID: c.System, + TGID: c.Talkgroup, + Name: c.TalkgroupLabel, + AlphaTag: c.TGAlphaTag, + TGGroup: c.TalkgroupGroup, + }) +} + func (c *Call) computeLength() (err error) { var td time.Duration diff --git a/pkg/database/calls.sql.go b/pkg/database/calls.sql.go index ab98ae4..2c82ea8 100644 --- a/pkg/database/calls.sql.go +++ b/pkg/database/calls.sql.go @@ -13,7 +13,7 @@ import ( ) const addAlert = `-- name: AddAlert :exec -INSERT INTO alerts (id, time, tgid, system_id, weight, score, orig_score, notified, metadata) +INSERT INTO alerts (time, tgid, system_id, weight, score, orig_score, notified, metadata) VALUES ( $1, @@ -23,13 +23,11 @@ VALUES $5, $6, $7, - $8, - $9 + $8 ) ` type AddAlertParams struct { - ID uuid.UUID `json:"id"` Time pgtype.Timestamptz `json:"time"` TGID int `json:"tgid"` SystemID int `json:"system_id"` @@ -42,7 +40,6 @@ type AddAlertParams struct { func (q *Queries) AddAlert(ctx context.Context, arg AddAlertParams) error { _, err := q.db.Exec(ctx, addAlert, - arg.ID, arg.Time, arg.TGID, arg.SystemID, @@ -109,9 +106,9 @@ type AddCallParams struct { Frequency int `json:"frequency"` Frequencies []int `json:"frequencies"` Patches []int `json:"patches"` - TgLabel *string `json:"tg_label"` - TgAlphaTag *string `json:"tg_alpha_tag"` - TgGroup *string `json:"tg_group"` + TGLabel *string `json:"tg_label"` + TGAlphaTag *string `json:"tg_alpha_tag"` + TGGroup *string `json:"tg_group"` Source int `json:"source"` } @@ -130,9 +127,9 @@ func (q *Queries) AddCall(ctx context.Context, arg AddCallParams) error { arg.Frequency, arg.Frequencies, arg.Patches, - arg.TgLabel, - arg.TgAlphaTag, - arg.TgGroup, + arg.TGLabel, + arg.TGAlphaTag, + arg.TGGroup, arg.Source, ) return err diff --git a/pkg/database/database.go b/pkg/database/database.go index 9aab59e..35fb95d 100644 --- a/pkg/database/database.go +++ b/pkg/database/database.go @@ -3,6 +3,7 @@ package database import ( "context" "errors" + "fmt" "strings" "dynatron.me/x/stillbox/pkg/config" @@ -19,11 +20,12 @@ import ( // DB is a database handle. //go:generate mockery -type DB interface { +type Store interface { Querier talkgroupQuerier DB() *Database + InTx(context.Context, func(Store) error, pgx.TxOptions) error } type Database struct { @@ -35,14 +37,41 @@ func (db *Database) DB() *Database { return db } +func (db *Database) InTx(ctx context.Context, f func(Store) error, opts pgx.TxOptions) error { + tx, err := db.DB().Pool.BeginTx(ctx, opts) + if err != nil { + return fmt.Errorf("Tx begin: %w", err) + } + + defer tx.Rollback(ctx) + + dbtx := &Database{Pool: db.Pool, Queries: db.Queries.WithTx(tx)} + + err = f(dbtx) + if err != nil { + return fmt.Errorf("Tx: %w", err) + } + + err = tx.Commit(ctx) + if err != nil { + return fmt.Errorf("Tx commit: %w", err) + } + + return nil +} + type dbLogger struct{} func (m dbLogger) Log(ctx context.Context, level tracelog.LogLevel, msg string, data map[string]any) { log.Debug().Fields(data).Msg(msg) } +func Close(c Store) { + c.(*Database).Pool.Close() +} + // NewClient creates a new DB using the provided config. -func NewClient(ctx context.Context, conf config.DB) (DB, error) { +func NewClient(ctx context.Context, conf config.DB) (Store, error) { dir, err := iofs.New(sqlembed.Migrations, "postgres/migrations") if err != nil { return nil, err @@ -90,8 +119,8 @@ type dBCtxKey string const DBCtxKey dBCtxKey = "dbctx" // FromCtx returns the database handle from the provided Context. -func FromCtx(ctx context.Context) DB { - c, ok := ctx.Value(DBCtxKey).(DB) +func FromCtx(ctx context.Context) Store { + c, ok := ctx.Value(DBCtxKey).(Store) if !ok { panic("no DB in context") } @@ -100,7 +129,7 @@ func FromCtx(ctx context.Context) DB { } // CtxWithDB returns a Context with the provided database handle. -func CtxWithDB(ctx context.Context, conn DB) context.Context { +func CtxWithDB(ctx context.Context, conn Store) context.Context { return context.WithValue(ctx, DBCtxKey, conn) } diff --git a/pkg/database/extend.go b/pkg/database/extend.go index d885991..c9ae0c2 100644 --- a/pkg/database/extend.go +++ b/pkg/database/extend.go @@ -2,16 +2,16 @@ package database func (d GetTalkgroupsRow) GetTalkgroup() Talkgroup { return d.Talkgroup } func (d GetTalkgroupsRow) GetSystem() System { return d.System } -func (d GetTalkgroupsRow) GetLearned() bool { return d.Learned } +func (d GetTalkgroupsRow) GetLearned() bool { return d.Talkgroup.Learned } func (g GetTalkgroupWithLearnedRow) GetTalkgroup() Talkgroup { return g.Talkgroup } func (g GetTalkgroupWithLearnedRow) GetSystem() System { return g.System } -func (g GetTalkgroupWithLearnedRow) GetLearned() bool { return g.Learned } +func (g GetTalkgroupWithLearnedRow) GetLearned() bool { return g.Talkgroup.Learned } func (g GetTalkgroupsWithLearnedRow) GetTalkgroup() Talkgroup { return g.Talkgroup } func (g GetTalkgroupsWithLearnedRow) GetSystem() System { return g.System } -func (g GetTalkgroupsWithLearnedRow) GetLearned() bool { return g.Learned } +func (g GetTalkgroupsWithLearnedRow) GetLearned() bool { return g.Talkgroup.Learned } func (g GetTalkgroupsWithLearnedBySystemRow) GetTalkgroup() Talkgroup { return g.Talkgroup } func (g GetTalkgroupsWithLearnedBySystemRow) GetSystem() System { return g.System } -func (g GetTalkgroupsWithLearnedBySystemRow) GetLearned() bool { return g.Learned } +func (g GetTalkgroupsWithLearnedBySystemRow) GetLearned() bool { return g.Talkgroup.Learned } func (g Talkgroup) GetTalkgroup() Talkgroup { return g } func (g Talkgroup) GetSystem() System { return System{ID: int(g.SystemID)} } func (g Talkgroup) GetLearned() bool { return false } diff --git a/pkg/database/mocks/DB.go b/pkg/database/mocks/DB.go deleted file mode 100644 index f7b451d..0000000 --- a/pkg/database/mocks/DB.go +++ /dev/null @@ -1,1631 +0,0 @@ -// Code generated by mockery v2.47.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - - database "dynatron.me/x/stillbox/pkg/database" - mock "github.com/stretchr/testify/mock" - - pgtype "github.com/jackc/pgx/v5/pgtype" - - uuid "github.com/google/uuid" -) - -// DB is an autogenerated mock type for the DB type -type DB struct { - mock.Mock -} - -type DB_Expecter struct { - mock *mock.Mock -} - -func (_m *DB) EXPECT() *DB_Expecter { - return &DB_Expecter{mock: &_m.Mock} -} - -// AddAlert provides a mock function with given fields: ctx, arg -func (_m *DB) AddAlert(ctx context.Context, arg database.AddAlertParams) error { - ret := _m.Called(ctx, arg) - - if len(ret) == 0 { - panic("no return value specified for AddAlert") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, database.AddAlertParams) error); ok { - r0 = rf(ctx, arg) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DB_AddAlert_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddAlert' -type DB_AddAlert_Call struct { - *mock.Call -} - -// AddAlert is a helper method to define mock.On call -// - ctx context.Context -// - arg database.AddAlertParams -func (_e *DB_Expecter) AddAlert(ctx interface{}, arg interface{}) *DB_AddAlert_Call { - return &DB_AddAlert_Call{Call: _e.mock.On("AddAlert", ctx, arg)} -} - -func (_c *DB_AddAlert_Call) Run(run func(ctx context.Context, arg database.AddAlertParams)) *DB_AddAlert_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(database.AddAlertParams)) - }) - return _c -} - -func (_c *DB_AddAlert_Call) Return(_a0 error) *DB_AddAlert_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *DB_AddAlert_Call) RunAndReturn(run func(context.Context, database.AddAlertParams) error) *DB_AddAlert_Call { - _c.Call.Return(run) - return _c -} - -// AddCall provides a mock function with given fields: ctx, arg -func (_m *DB) AddCall(ctx context.Context, arg database.AddCallParams) error { - ret := _m.Called(ctx, arg) - - if len(ret) == 0 { - panic("no return value specified for AddCall") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, database.AddCallParams) error); ok { - r0 = rf(ctx, arg) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DB_AddCall_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddCall' -type DB_AddCall_Call struct { - *mock.Call -} - -// AddCall is a helper method to define mock.On call -// - ctx context.Context -// - arg database.AddCallParams -func (_e *DB_Expecter) AddCall(ctx interface{}, arg interface{}) *DB_AddCall_Call { - return &DB_AddCall_Call{Call: _e.mock.On("AddCall", ctx, arg)} -} - -func (_c *DB_AddCall_Call) Run(run func(ctx context.Context, arg database.AddCallParams)) *DB_AddCall_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(database.AddCallParams)) - }) - return _c -} - -func (_c *DB_AddCall_Call) Return(_a0 error) *DB_AddCall_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *DB_AddCall_Call) RunAndReturn(run func(context.Context, database.AddCallParams) error) *DB_AddCall_Call { - _c.Call.Return(run) - return _c -} - -// BulkSetTalkgroupTags provides a mock function with given fields: ctx, tgs, tags -func (_m *DB) BulkSetTalkgroupTags(ctx context.Context, tgs database.TGTuples, tags []string) error { - ret := _m.Called(ctx, tgs, tags) - - if len(ret) == 0 { - panic("no return value specified for BulkSetTalkgroupTags") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, database.TGTuples, []string) error); ok { - r0 = rf(ctx, tgs, tags) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DB_BulkSetTalkgroupTags_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BulkSetTalkgroupTags' -type DB_BulkSetTalkgroupTags_Call struct { - *mock.Call -} - -// BulkSetTalkgroupTags is a helper method to define mock.On call -// - ctx context.Context -// - tgs database.TGTuples -// - tags []string -func (_e *DB_Expecter) BulkSetTalkgroupTags(ctx interface{}, tgs interface{}, tags interface{}) *DB_BulkSetTalkgroupTags_Call { - return &DB_BulkSetTalkgroupTags_Call{Call: _e.mock.On("BulkSetTalkgroupTags", ctx, tgs, tags)} -} - -func (_c *DB_BulkSetTalkgroupTags_Call) Run(run func(ctx context.Context, tgs database.TGTuples, tags []string)) *DB_BulkSetTalkgroupTags_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(database.TGTuples), args[2].([]string)) - }) - return _c -} - -func (_c *DB_BulkSetTalkgroupTags_Call) Return(_a0 error) *DB_BulkSetTalkgroupTags_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *DB_BulkSetTalkgroupTags_Call) RunAndReturn(run func(context.Context, database.TGTuples, []string) error) *DB_BulkSetTalkgroupTags_Call { - _c.Call.Return(run) - return _c -} - -// CreateAPIKey provides a mock function with given fields: ctx, owner, expires, disabled -func (_m *DB) CreateAPIKey(ctx context.Context, owner int, expires pgtype.Timestamp, disabled *bool) (database.ApiKey, error) { - ret := _m.Called(ctx, owner, expires, disabled) - - if len(ret) == 0 { - panic("no return value specified for CreateAPIKey") - } - - var r0 database.ApiKey - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, int, pgtype.Timestamp, *bool) (database.ApiKey, error)); ok { - return rf(ctx, owner, expires, disabled) - } - if rf, ok := ret.Get(0).(func(context.Context, int, pgtype.Timestamp, *bool) database.ApiKey); ok { - r0 = rf(ctx, owner, expires, disabled) - } else { - r0 = ret.Get(0).(database.ApiKey) - } - - if rf, ok := ret.Get(1).(func(context.Context, int, pgtype.Timestamp, *bool) error); ok { - r1 = rf(ctx, owner, expires, disabled) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// DB_CreateAPIKey_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateAPIKey' -type DB_CreateAPIKey_Call struct { - *mock.Call -} - -// CreateAPIKey is a helper method to define mock.On call -// - ctx context.Context -// - owner int -// - expires pgtype.Timestamp -// - disabled *bool -func (_e *DB_Expecter) CreateAPIKey(ctx interface{}, owner interface{}, expires interface{}, disabled interface{}) *DB_CreateAPIKey_Call { - return &DB_CreateAPIKey_Call{Call: _e.mock.On("CreateAPIKey", ctx, owner, expires, disabled)} -} - -func (_c *DB_CreateAPIKey_Call) Run(run func(ctx context.Context, owner int, expires pgtype.Timestamp, disabled *bool)) *DB_CreateAPIKey_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(int), args[2].(pgtype.Timestamp), args[3].(*bool)) - }) - return _c -} - -func (_c *DB_CreateAPIKey_Call) Return(_a0 database.ApiKey, _a1 error) *DB_CreateAPIKey_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *DB_CreateAPIKey_Call) RunAndReturn(run func(context.Context, int, pgtype.Timestamp, *bool) (database.ApiKey, error)) *DB_CreateAPIKey_Call { - _c.Call.Return(run) - return _c -} - -// CreateUser provides a mock function with given fields: ctx, arg -func (_m *DB) CreateUser(ctx context.Context, arg database.CreateUserParams) (database.User, error) { - ret := _m.Called(ctx, arg) - - if len(ret) == 0 { - panic("no return value specified for CreateUser") - } - - var r0 database.User - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, database.CreateUserParams) (database.User, error)); ok { - return rf(ctx, arg) - } - if rf, ok := ret.Get(0).(func(context.Context, database.CreateUserParams) database.User); ok { - r0 = rf(ctx, arg) - } else { - r0 = ret.Get(0).(database.User) - } - - if rf, ok := ret.Get(1).(func(context.Context, database.CreateUserParams) error); ok { - r1 = rf(ctx, arg) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// DB_CreateUser_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateUser' -type DB_CreateUser_Call struct { - *mock.Call -} - -// CreateUser is a helper method to define mock.On call -// - ctx context.Context -// - arg database.CreateUserParams -func (_e *DB_Expecter) CreateUser(ctx interface{}, arg interface{}) *DB_CreateUser_Call { - return &DB_CreateUser_Call{Call: _e.mock.On("CreateUser", ctx, arg)} -} - -func (_c *DB_CreateUser_Call) Run(run func(ctx context.Context, arg database.CreateUserParams)) *DB_CreateUser_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(database.CreateUserParams)) - }) - return _c -} - -func (_c *DB_CreateUser_Call) Return(_a0 database.User, _a1 error) *DB_CreateUser_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *DB_CreateUser_Call) RunAndReturn(run func(context.Context, database.CreateUserParams) (database.User, error)) *DB_CreateUser_Call { - _c.Call.Return(run) - return _c -} - -// DB provides a mock function with given fields: -func (_m *DB) DB() *database.Database { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for DB") - } - - var r0 *database.Database - if rf, ok := ret.Get(0).(func() *database.Database); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*database.Database) - } - } - - return r0 -} - -// DB_DB_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DB' -type DB_DB_Call struct { - *mock.Call -} - -// DB is a helper method to define mock.On call -func (_e *DB_Expecter) DB() *DB_DB_Call { - return &DB_DB_Call{Call: _e.mock.On("DB")} -} - -func (_c *DB_DB_Call) Run(run func()) *DB_DB_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *DB_DB_Call) Return(_a0 *database.Database) *DB_DB_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *DB_DB_Call) RunAndReturn(run func() *database.Database) *DB_DB_Call { - _c.Call.Return(run) - return _c -} - -// DeleteAPIKey provides a mock function with given fields: ctx, apiKey -func (_m *DB) DeleteAPIKey(ctx context.Context, apiKey string) error { - ret := _m.Called(ctx, apiKey) - - if len(ret) == 0 { - panic("no return value specified for DeleteAPIKey") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { - r0 = rf(ctx, apiKey) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DB_DeleteAPIKey_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteAPIKey' -type DB_DeleteAPIKey_Call struct { - *mock.Call -} - -// DeleteAPIKey is a helper method to define mock.On call -// - ctx context.Context -// - apiKey string -func (_e *DB_Expecter) DeleteAPIKey(ctx interface{}, apiKey interface{}) *DB_DeleteAPIKey_Call { - return &DB_DeleteAPIKey_Call{Call: _e.mock.On("DeleteAPIKey", ctx, apiKey)} -} - -func (_c *DB_DeleteAPIKey_Call) Run(run func(ctx context.Context, apiKey string)) *DB_DeleteAPIKey_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string)) - }) - return _c -} - -func (_c *DB_DeleteAPIKey_Call) Return(_a0 error) *DB_DeleteAPIKey_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *DB_DeleteAPIKey_Call) RunAndReturn(run func(context.Context, string) error) *DB_DeleteAPIKey_Call { - _c.Call.Return(run) - return _c -} - -// DeleteUser provides a mock function with given fields: ctx, username -func (_m *DB) DeleteUser(ctx context.Context, username string) error { - ret := _m.Called(ctx, username) - - if len(ret) == 0 { - panic("no return value specified for DeleteUser") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { - r0 = rf(ctx, username) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DB_DeleteUser_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteUser' -type DB_DeleteUser_Call struct { - *mock.Call -} - -// DeleteUser is a helper method to define mock.On call -// - ctx context.Context -// - username string -func (_e *DB_Expecter) DeleteUser(ctx interface{}, username interface{}) *DB_DeleteUser_Call { - return &DB_DeleteUser_Call{Call: _e.mock.On("DeleteUser", ctx, username)} -} - -func (_c *DB_DeleteUser_Call) Run(run func(ctx context.Context, username string)) *DB_DeleteUser_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string)) - }) - return _c -} - -func (_c *DB_DeleteUser_Call) Return(_a0 error) *DB_DeleteUser_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *DB_DeleteUser_Call) RunAndReturn(run func(context.Context, string) error) *DB_DeleteUser_Call { - _c.Call.Return(run) - return _c -} - -// GetAPIKey provides a mock function with given fields: ctx, apiKey -func (_m *DB) GetAPIKey(ctx context.Context, apiKey string) (database.ApiKey, error) { - ret := _m.Called(ctx, apiKey) - - if len(ret) == 0 { - panic("no return value specified for GetAPIKey") - } - - var r0 database.ApiKey - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (database.ApiKey, error)); ok { - return rf(ctx, apiKey) - } - if rf, ok := ret.Get(0).(func(context.Context, string) database.ApiKey); ok { - r0 = rf(ctx, apiKey) - } else { - r0 = ret.Get(0).(database.ApiKey) - } - - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, apiKey) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// DB_GetAPIKey_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetAPIKey' -type DB_GetAPIKey_Call struct { - *mock.Call -} - -// GetAPIKey is a helper method to define mock.On call -// - ctx context.Context -// - apiKey string -func (_e *DB_Expecter) GetAPIKey(ctx interface{}, apiKey interface{}) *DB_GetAPIKey_Call { - return &DB_GetAPIKey_Call{Call: _e.mock.On("GetAPIKey", ctx, apiKey)} -} - -func (_c *DB_GetAPIKey_Call) Run(run func(ctx context.Context, apiKey string)) *DB_GetAPIKey_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string)) - }) - return _c -} - -func (_c *DB_GetAPIKey_Call) Return(_a0 database.ApiKey, _a1 error) *DB_GetAPIKey_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *DB_GetAPIKey_Call) RunAndReturn(run func(context.Context, string) (database.ApiKey, error)) *DB_GetAPIKey_Call { - _c.Call.Return(run) - return _c -} - -// GetDatabaseSize provides a mock function with given fields: ctx -func (_m *DB) GetDatabaseSize(ctx context.Context) (string, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for GetDatabaseSize") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (string, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) string); ok { - r0 = rf(ctx) - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// DB_GetDatabaseSize_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetDatabaseSize' -type DB_GetDatabaseSize_Call struct { - *mock.Call -} - -// GetDatabaseSize is a helper method to define mock.On call -// - ctx context.Context -func (_e *DB_Expecter) GetDatabaseSize(ctx interface{}) *DB_GetDatabaseSize_Call { - return &DB_GetDatabaseSize_Call{Call: _e.mock.On("GetDatabaseSize", ctx)} -} - -func (_c *DB_GetDatabaseSize_Call) Run(run func(ctx context.Context)) *DB_GetDatabaseSize_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *DB_GetDatabaseSize_Call) Return(_a0 string, _a1 error) *DB_GetDatabaseSize_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *DB_GetDatabaseSize_Call) RunAndReturn(run func(context.Context) (string, error)) *DB_GetDatabaseSize_Call { - _c.Call.Return(run) - return _c -} - -// GetSystemName provides a mock function with given fields: ctx, systemID -func (_m *DB) GetSystemName(ctx context.Context, systemID int) (string, error) { - ret := _m.Called(ctx, systemID) - - if len(ret) == 0 { - panic("no return value specified for GetSystemName") - } - - var r0 string - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, int) (string, error)); ok { - return rf(ctx, systemID) - } - if rf, ok := ret.Get(0).(func(context.Context, int) string); ok { - r0 = rf(ctx, systemID) - } else { - r0 = ret.Get(0).(string) - } - - if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { - r1 = rf(ctx, systemID) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// DB_GetSystemName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetSystemName' -type DB_GetSystemName_Call struct { - *mock.Call -} - -// GetSystemName is a helper method to define mock.On call -// - ctx context.Context -// - systemID int -func (_e *DB_Expecter) GetSystemName(ctx interface{}, systemID interface{}) *DB_GetSystemName_Call { - return &DB_GetSystemName_Call{Call: _e.mock.On("GetSystemName", ctx, systemID)} -} - -func (_c *DB_GetSystemName_Call) Run(run func(ctx context.Context, systemID int)) *DB_GetSystemName_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(int)) - }) - return _c -} - -func (_c *DB_GetSystemName_Call) Return(_a0 string, _a1 error) *DB_GetSystemName_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *DB_GetSystemName_Call) RunAndReturn(run func(context.Context, int) (string, error)) *DB_GetSystemName_Call { - _c.Call.Return(run) - return _c -} - -// GetTalkgroup provides a mock function with given fields: ctx, systemID, tgID -func (_m *DB) GetTalkgroup(ctx context.Context, systemID int32, tgID int32) (database.GetTalkgroupRow, error) { - ret := _m.Called(ctx, systemID, tgID) - - if len(ret) == 0 { - panic("no return value specified for GetTalkgroup") - } - - var r0 database.GetTalkgroupRow - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, int32, int32) (database.GetTalkgroupRow, error)); ok { - return rf(ctx, systemID, tgID) - } - if rf, ok := ret.Get(0).(func(context.Context, int32, int32) database.GetTalkgroupRow); ok { - r0 = rf(ctx, systemID, tgID) - } else { - r0 = ret.Get(0).(database.GetTalkgroupRow) - } - - if rf, ok := ret.Get(1).(func(context.Context, int32, int32) error); ok { - r1 = rf(ctx, systemID, tgID) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// DB_GetTalkgroup_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTalkgroup' -type DB_GetTalkgroup_Call struct { - *mock.Call -} - -// GetTalkgroup is a helper method to define mock.On call -// - ctx context.Context -// - systemID int32 -// - tgID int32 -func (_e *DB_Expecter) GetTalkgroup(ctx interface{}, systemID interface{}, tgID interface{}) *DB_GetTalkgroup_Call { - return &DB_GetTalkgroup_Call{Call: _e.mock.On("GetTalkgroup", ctx, systemID, tgID)} -} - -func (_c *DB_GetTalkgroup_Call) Run(run func(ctx context.Context, systemID int32, tgID int32)) *DB_GetTalkgroup_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(int32), args[2].(int32)) - }) - return _c -} - -func (_c *DB_GetTalkgroup_Call) Return(_a0 database.GetTalkgroupRow, _a1 error) *DB_GetTalkgroup_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *DB_GetTalkgroup_Call) RunAndReturn(run func(context.Context, int32, int32) (database.GetTalkgroupRow, error)) *DB_GetTalkgroup_Call { - _c.Call.Return(run) - return _c -} - -// GetTalkgroupIDsByTags provides a mock function with given fields: ctx, anyTags, allTags, notTags -func (_m *DB) GetTalkgroupIDsByTags(ctx context.Context, anyTags []string, allTags []string, notTags []string) ([]database.GetTalkgroupIDsByTagsRow, error) { - ret := _m.Called(ctx, anyTags, allTags, notTags) - - if len(ret) == 0 { - panic("no return value specified for GetTalkgroupIDsByTags") - } - - var r0 []database.GetTalkgroupIDsByTagsRow - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, []string, []string, []string) ([]database.GetTalkgroupIDsByTagsRow, error)); ok { - return rf(ctx, anyTags, allTags, notTags) - } - if rf, ok := ret.Get(0).(func(context.Context, []string, []string, []string) []database.GetTalkgroupIDsByTagsRow); ok { - r0 = rf(ctx, anyTags, allTags, notTags) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]database.GetTalkgroupIDsByTagsRow) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, []string, []string, []string) error); ok { - r1 = rf(ctx, anyTags, allTags, notTags) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// DB_GetTalkgroupIDsByTags_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTalkgroupIDsByTags' -type DB_GetTalkgroupIDsByTags_Call struct { - *mock.Call -} - -// GetTalkgroupIDsByTags is a helper method to define mock.On call -// - ctx context.Context -// - anyTags []string -// - allTags []string -// - notTags []string -func (_e *DB_Expecter) GetTalkgroupIDsByTags(ctx interface{}, anyTags interface{}, allTags interface{}, notTags interface{}) *DB_GetTalkgroupIDsByTags_Call { - return &DB_GetTalkgroupIDsByTags_Call{Call: _e.mock.On("GetTalkgroupIDsByTags", ctx, anyTags, allTags, notTags)} -} - -func (_c *DB_GetTalkgroupIDsByTags_Call) Run(run func(ctx context.Context, anyTags []string, allTags []string, notTags []string)) *DB_GetTalkgroupIDsByTags_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].([]string), args[2].([]string), args[3].([]string)) - }) - return _c -} - -func (_c *DB_GetTalkgroupIDsByTags_Call) Return(_a0 []database.GetTalkgroupIDsByTagsRow, _a1 error) *DB_GetTalkgroupIDsByTags_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *DB_GetTalkgroupIDsByTags_Call) RunAndReturn(run func(context.Context, []string, []string, []string) ([]database.GetTalkgroupIDsByTagsRow, error)) *DB_GetTalkgroupIDsByTags_Call { - _c.Call.Return(run) - return _c -} - -// GetTalkgroupTags provides a mock function with given fields: ctx, systemID, tgID -func (_m *DB) GetTalkgroupTags(ctx context.Context, systemID int32, tgID int32) ([]string, error) { - ret := _m.Called(ctx, systemID, tgID) - - if len(ret) == 0 { - panic("no return value specified for GetTalkgroupTags") - } - - var r0 []string - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, int32, int32) ([]string, error)); ok { - return rf(ctx, systemID, tgID) - } - if rf, ok := ret.Get(0).(func(context.Context, int32, int32) []string); ok { - r0 = rf(ctx, systemID, tgID) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]string) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, int32, int32) error); ok { - r1 = rf(ctx, systemID, tgID) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// DB_GetTalkgroupTags_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTalkgroupTags' -type DB_GetTalkgroupTags_Call struct { - *mock.Call -} - -// GetTalkgroupTags is a helper method to define mock.On call -// - ctx context.Context -// - systemID int32 -// - tgID int32 -func (_e *DB_Expecter) GetTalkgroupTags(ctx interface{}, systemID interface{}, tgID interface{}) *DB_GetTalkgroupTags_Call { - return &DB_GetTalkgroupTags_Call{Call: _e.mock.On("GetTalkgroupTags", ctx, systemID, tgID)} -} - -func (_c *DB_GetTalkgroupTags_Call) Run(run func(ctx context.Context, systemID int32, tgID int32)) *DB_GetTalkgroupTags_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(int32), args[2].(int32)) - }) - return _c -} - -func (_c *DB_GetTalkgroupTags_Call) Return(_a0 []string, _a1 error) *DB_GetTalkgroupTags_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *DB_GetTalkgroupTags_Call) RunAndReturn(run func(context.Context, int32, int32) ([]string, error)) *DB_GetTalkgroupTags_Call { - _c.Call.Return(run) - return _c -} - -// GetTalkgroupWithLearned provides a mock function with given fields: ctx, systemID, tGID -func (_m *DB) GetTalkgroupWithLearned(ctx context.Context, systemID int32, tGID int32) (database.GetTalkgroupWithLearnedRow, error) { - ret := _m.Called(ctx, systemID, tGID) - - if len(ret) == 0 { - panic("no return value specified for GetTalkgroupWithLearned") - } - - var r0 database.GetTalkgroupWithLearnedRow - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, int32, int32) (database.GetTalkgroupWithLearnedRow, error)); ok { - return rf(ctx, systemID, tGID) - } - if rf, ok := ret.Get(0).(func(context.Context, int32, int32) database.GetTalkgroupWithLearnedRow); ok { - r0 = rf(ctx, systemID, tGID) - } else { - r0 = ret.Get(0).(database.GetTalkgroupWithLearnedRow) - } - - if rf, ok := ret.Get(1).(func(context.Context, int32, int32) error); ok { - r1 = rf(ctx, systemID, tGID) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// DB_GetTalkgroupWithLearned_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTalkgroupWithLearned' -type DB_GetTalkgroupWithLearned_Call struct { - *mock.Call -} - -// GetTalkgroupWithLearned is a helper method to define mock.On call -// - ctx context.Context -// - systemID int32 -// - tGID int32 -func (_e *DB_Expecter) GetTalkgroupWithLearned(ctx interface{}, systemID interface{}, tGID interface{}) *DB_GetTalkgroupWithLearned_Call { - return &DB_GetTalkgroupWithLearned_Call{Call: _e.mock.On("GetTalkgroupWithLearned", ctx, systemID, tGID)} -} - -func (_c *DB_GetTalkgroupWithLearned_Call) Run(run func(ctx context.Context, systemID int32, tGID int32)) *DB_GetTalkgroupWithLearned_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(int32), args[2].(int32)) - }) - return _c -} - -func (_c *DB_GetTalkgroupWithLearned_Call) Return(_a0 database.GetTalkgroupWithLearnedRow, _a1 error) *DB_GetTalkgroupWithLearned_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *DB_GetTalkgroupWithLearned_Call) RunAndReturn(run func(context.Context, int32, int32) (database.GetTalkgroupWithLearnedRow, error)) *DB_GetTalkgroupWithLearned_Call { - _c.Call.Return(run) - return _c -} - -// GetTalkgroupsBySysTGID provides a mock function with given fields: ctx, ids -func (_m *DB) GetTalkgroupsBySysTGID(ctx context.Context, ids database.TGTuples) ([]database.GetTalkgroupsRow, error) { - ret := _m.Called(ctx, ids) - - if len(ret) == 0 { - panic("no return value specified for GetTalkgroupsBySysTGID") - } - - var r0 []database.GetTalkgroupsRow - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, database.TGTuples) ([]database.GetTalkgroupsRow, error)); ok { - return rf(ctx, ids) - } - if rf, ok := ret.Get(0).(func(context.Context, database.TGTuples) []database.GetTalkgroupsRow); ok { - r0 = rf(ctx, ids) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]database.GetTalkgroupsRow) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, database.TGTuples) error); ok { - r1 = rf(ctx, ids) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// DB_GetTalkgroupsBySysTGID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTalkgroupsBySysTGID' -type DB_GetTalkgroupsBySysTGID_Call struct { - *mock.Call -} - -// GetTalkgroupsBySysTGID is a helper method to define mock.On call -// - ctx context.Context -// - ids database.TGTuples -func (_e *DB_Expecter) GetTalkgroupsBySysTGID(ctx interface{}, ids interface{}) *DB_GetTalkgroupsBySysTGID_Call { - return &DB_GetTalkgroupsBySysTGID_Call{Call: _e.mock.On("GetTalkgroupsBySysTGID", ctx, ids)} -} - -func (_c *DB_GetTalkgroupsBySysTGID_Call) Run(run func(ctx context.Context, ids database.TGTuples)) *DB_GetTalkgroupsBySysTGID_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(database.TGTuples)) - }) - return _c -} - -func (_c *DB_GetTalkgroupsBySysTGID_Call) Return(_a0 []database.GetTalkgroupsRow, _a1 error) *DB_GetTalkgroupsBySysTGID_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *DB_GetTalkgroupsBySysTGID_Call) RunAndReturn(run func(context.Context, database.TGTuples) ([]database.GetTalkgroupsRow, error)) *DB_GetTalkgroupsBySysTGID_Call { - _c.Call.Return(run) - return _c -} - -// GetTalkgroupsWithAllTags provides a mock function with given fields: ctx, tags -func (_m *DB) GetTalkgroupsWithAllTags(ctx context.Context, tags []string) ([]database.GetTalkgroupsWithAllTagsRow, error) { - ret := _m.Called(ctx, tags) - - if len(ret) == 0 { - panic("no return value specified for GetTalkgroupsWithAllTags") - } - - var r0 []database.GetTalkgroupsWithAllTagsRow - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, []string) ([]database.GetTalkgroupsWithAllTagsRow, error)); ok { - return rf(ctx, tags) - } - if rf, ok := ret.Get(0).(func(context.Context, []string) []database.GetTalkgroupsWithAllTagsRow); ok { - r0 = rf(ctx, tags) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]database.GetTalkgroupsWithAllTagsRow) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, []string) error); ok { - r1 = rf(ctx, tags) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// DB_GetTalkgroupsWithAllTags_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTalkgroupsWithAllTags' -type DB_GetTalkgroupsWithAllTags_Call struct { - *mock.Call -} - -// GetTalkgroupsWithAllTags is a helper method to define mock.On call -// - ctx context.Context -// - tags []string -func (_e *DB_Expecter) GetTalkgroupsWithAllTags(ctx interface{}, tags interface{}) *DB_GetTalkgroupsWithAllTags_Call { - return &DB_GetTalkgroupsWithAllTags_Call{Call: _e.mock.On("GetTalkgroupsWithAllTags", ctx, tags)} -} - -func (_c *DB_GetTalkgroupsWithAllTags_Call) Run(run func(ctx context.Context, tags []string)) *DB_GetTalkgroupsWithAllTags_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].([]string)) - }) - return _c -} - -func (_c *DB_GetTalkgroupsWithAllTags_Call) Return(_a0 []database.GetTalkgroupsWithAllTagsRow, _a1 error) *DB_GetTalkgroupsWithAllTags_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *DB_GetTalkgroupsWithAllTags_Call) RunAndReturn(run func(context.Context, []string) ([]database.GetTalkgroupsWithAllTagsRow, error)) *DB_GetTalkgroupsWithAllTags_Call { - _c.Call.Return(run) - return _c -} - -// GetTalkgroupsWithAnyTags provides a mock function with given fields: ctx, tags -func (_m *DB) GetTalkgroupsWithAnyTags(ctx context.Context, tags []string) ([]database.GetTalkgroupsWithAnyTagsRow, error) { - ret := _m.Called(ctx, tags) - - if len(ret) == 0 { - panic("no return value specified for GetTalkgroupsWithAnyTags") - } - - var r0 []database.GetTalkgroupsWithAnyTagsRow - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, []string) ([]database.GetTalkgroupsWithAnyTagsRow, error)); ok { - return rf(ctx, tags) - } - if rf, ok := ret.Get(0).(func(context.Context, []string) []database.GetTalkgroupsWithAnyTagsRow); ok { - r0 = rf(ctx, tags) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]database.GetTalkgroupsWithAnyTagsRow) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, []string) error); ok { - r1 = rf(ctx, tags) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// DB_GetTalkgroupsWithAnyTags_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTalkgroupsWithAnyTags' -type DB_GetTalkgroupsWithAnyTags_Call struct { - *mock.Call -} - -// GetTalkgroupsWithAnyTags is a helper method to define mock.On call -// - ctx context.Context -// - tags []string -func (_e *DB_Expecter) GetTalkgroupsWithAnyTags(ctx interface{}, tags interface{}) *DB_GetTalkgroupsWithAnyTags_Call { - return &DB_GetTalkgroupsWithAnyTags_Call{Call: _e.mock.On("GetTalkgroupsWithAnyTags", ctx, tags)} -} - -func (_c *DB_GetTalkgroupsWithAnyTags_Call) Run(run func(ctx context.Context, tags []string)) *DB_GetTalkgroupsWithAnyTags_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].([]string)) - }) - return _c -} - -func (_c *DB_GetTalkgroupsWithAnyTags_Call) Return(_a0 []database.GetTalkgroupsWithAnyTagsRow, _a1 error) *DB_GetTalkgroupsWithAnyTags_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *DB_GetTalkgroupsWithAnyTags_Call) RunAndReturn(run func(context.Context, []string) ([]database.GetTalkgroupsWithAnyTagsRow, error)) *DB_GetTalkgroupsWithAnyTags_Call { - _c.Call.Return(run) - return _c -} - -// GetTalkgroupsWithLearned provides a mock function with given fields: ctx -func (_m *DB) GetTalkgroupsWithLearned(ctx context.Context) ([]database.GetTalkgroupsWithLearnedRow, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for GetTalkgroupsWithLearned") - } - - var r0 []database.GetTalkgroupsWithLearnedRow - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) ([]database.GetTalkgroupsWithLearnedRow, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) []database.GetTalkgroupsWithLearnedRow); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]database.GetTalkgroupsWithLearnedRow) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// DB_GetTalkgroupsWithLearned_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTalkgroupsWithLearned' -type DB_GetTalkgroupsWithLearned_Call struct { - *mock.Call -} - -// GetTalkgroupsWithLearned is a helper method to define mock.On call -// - ctx context.Context -func (_e *DB_Expecter) GetTalkgroupsWithLearned(ctx interface{}) *DB_GetTalkgroupsWithLearned_Call { - return &DB_GetTalkgroupsWithLearned_Call{Call: _e.mock.On("GetTalkgroupsWithLearned", ctx)} -} - -func (_c *DB_GetTalkgroupsWithLearned_Call) Run(run func(ctx context.Context)) *DB_GetTalkgroupsWithLearned_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *DB_GetTalkgroupsWithLearned_Call) Return(_a0 []database.GetTalkgroupsWithLearnedRow, _a1 error) *DB_GetTalkgroupsWithLearned_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *DB_GetTalkgroupsWithLearned_Call) RunAndReturn(run func(context.Context) ([]database.GetTalkgroupsWithLearnedRow, error)) *DB_GetTalkgroupsWithLearned_Call { - _c.Call.Return(run) - return _c -} - -// GetTalkgroupsWithLearnedBySysTGID provides a mock function with given fields: ctx, ids -func (_m *DB) GetTalkgroupsWithLearnedBySysTGID(ctx context.Context, ids database.TGTuples) ([]database.GetTalkgroupsRow, error) { - ret := _m.Called(ctx, ids) - - if len(ret) == 0 { - panic("no return value specified for GetTalkgroupsWithLearnedBySysTGID") - } - - var r0 []database.GetTalkgroupsRow - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, database.TGTuples) ([]database.GetTalkgroupsRow, error)); ok { - return rf(ctx, ids) - } - if rf, ok := ret.Get(0).(func(context.Context, database.TGTuples) []database.GetTalkgroupsRow); ok { - r0 = rf(ctx, ids) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]database.GetTalkgroupsRow) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, database.TGTuples) error); ok { - r1 = rf(ctx, ids) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// DB_GetTalkgroupsWithLearnedBySysTGID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTalkgroupsWithLearnedBySysTGID' -type DB_GetTalkgroupsWithLearnedBySysTGID_Call struct { - *mock.Call -} - -// GetTalkgroupsWithLearnedBySysTGID is a helper method to define mock.On call -// - ctx context.Context -// - ids database.TGTuples -func (_e *DB_Expecter) GetTalkgroupsWithLearnedBySysTGID(ctx interface{}, ids interface{}) *DB_GetTalkgroupsWithLearnedBySysTGID_Call { - return &DB_GetTalkgroupsWithLearnedBySysTGID_Call{Call: _e.mock.On("GetTalkgroupsWithLearnedBySysTGID", ctx, ids)} -} - -func (_c *DB_GetTalkgroupsWithLearnedBySysTGID_Call) Run(run func(ctx context.Context, ids database.TGTuples)) *DB_GetTalkgroupsWithLearnedBySysTGID_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(database.TGTuples)) - }) - return _c -} - -func (_c *DB_GetTalkgroupsWithLearnedBySysTGID_Call) Return(_a0 []database.GetTalkgroupsRow, _a1 error) *DB_GetTalkgroupsWithLearnedBySysTGID_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *DB_GetTalkgroupsWithLearnedBySysTGID_Call) RunAndReturn(run func(context.Context, database.TGTuples) ([]database.GetTalkgroupsRow, error)) *DB_GetTalkgroupsWithLearnedBySysTGID_Call { - _c.Call.Return(run) - return _c -} - -// GetTalkgroupsWithLearnedBySystem provides a mock function with given fields: ctx, system -func (_m *DB) GetTalkgroupsWithLearnedBySystem(ctx context.Context, system int32) ([]database.GetTalkgroupsWithLearnedBySystemRow, error) { - ret := _m.Called(ctx, system) - - if len(ret) == 0 { - panic("no return value specified for GetTalkgroupsWithLearnedBySystem") - } - - var r0 []database.GetTalkgroupsWithLearnedBySystemRow - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, int32) ([]database.GetTalkgroupsWithLearnedBySystemRow, error)); ok { - return rf(ctx, system) - } - if rf, ok := ret.Get(0).(func(context.Context, int32) []database.GetTalkgroupsWithLearnedBySystemRow); ok { - r0 = rf(ctx, system) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]database.GetTalkgroupsWithLearnedBySystemRow) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, int32) error); ok { - r1 = rf(ctx, system) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// DB_GetTalkgroupsWithLearnedBySystem_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTalkgroupsWithLearnedBySystem' -type DB_GetTalkgroupsWithLearnedBySystem_Call struct { - *mock.Call -} - -// GetTalkgroupsWithLearnedBySystem is a helper method to define mock.On call -// - ctx context.Context -// - system int32 -func (_e *DB_Expecter) GetTalkgroupsWithLearnedBySystem(ctx interface{}, system interface{}) *DB_GetTalkgroupsWithLearnedBySystem_Call { - return &DB_GetTalkgroupsWithLearnedBySystem_Call{Call: _e.mock.On("GetTalkgroupsWithLearnedBySystem", ctx, system)} -} - -func (_c *DB_GetTalkgroupsWithLearnedBySystem_Call) Run(run func(ctx context.Context, system int32)) *DB_GetTalkgroupsWithLearnedBySystem_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(int32)) - }) - return _c -} - -func (_c *DB_GetTalkgroupsWithLearnedBySystem_Call) Return(_a0 []database.GetTalkgroupsWithLearnedBySystemRow, _a1 error) *DB_GetTalkgroupsWithLearnedBySystem_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *DB_GetTalkgroupsWithLearnedBySystem_Call) RunAndReturn(run func(context.Context, int32) ([]database.GetTalkgroupsWithLearnedBySystemRow, error)) *DB_GetTalkgroupsWithLearnedBySystem_Call { - _c.Call.Return(run) - return _c -} - -// GetUserByID provides a mock function with given fields: ctx, id -func (_m *DB) GetUserByID(ctx context.Context, id int32) (database.User, error) { - ret := _m.Called(ctx, id) - - if len(ret) == 0 { - panic("no return value specified for GetUserByID") - } - - var r0 database.User - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, int32) (database.User, error)); ok { - return rf(ctx, id) - } - if rf, ok := ret.Get(0).(func(context.Context, int32) database.User); ok { - r0 = rf(ctx, id) - } else { - r0 = ret.Get(0).(database.User) - } - - if rf, ok := ret.Get(1).(func(context.Context, int32) error); ok { - r1 = rf(ctx, id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// DB_GetUserByID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetUserByID' -type DB_GetUserByID_Call struct { - *mock.Call -} - -// GetUserByID is a helper method to define mock.On call -// - ctx context.Context -// - id int32 -func (_e *DB_Expecter) GetUserByID(ctx interface{}, id interface{}) *DB_GetUserByID_Call { - return &DB_GetUserByID_Call{Call: _e.mock.On("GetUserByID", ctx, id)} -} - -func (_c *DB_GetUserByID_Call) Run(run func(ctx context.Context, id int32)) *DB_GetUserByID_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(int32)) - }) - return _c -} - -func (_c *DB_GetUserByID_Call) Return(_a0 database.User, _a1 error) *DB_GetUserByID_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *DB_GetUserByID_Call) RunAndReturn(run func(context.Context, int32) (database.User, error)) *DB_GetUserByID_Call { - _c.Call.Return(run) - return _c -} - -// GetUserByUID provides a mock function with given fields: ctx, id -func (_m *DB) GetUserByUID(ctx context.Context, id int32) (database.User, error) { - ret := _m.Called(ctx, id) - - if len(ret) == 0 { - panic("no return value specified for GetUserByUID") - } - - var r0 database.User - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, int32) (database.User, error)); ok { - return rf(ctx, id) - } - if rf, ok := ret.Get(0).(func(context.Context, int32) database.User); ok { - r0 = rf(ctx, id) - } else { - r0 = ret.Get(0).(database.User) - } - - if rf, ok := ret.Get(1).(func(context.Context, int32) error); ok { - r1 = rf(ctx, id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// DB_GetUserByUID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetUserByUID' -type DB_GetUserByUID_Call struct { - *mock.Call -} - -// GetUserByUID is a helper method to define mock.On call -// - ctx context.Context -// - id int32 -func (_e *DB_Expecter) GetUserByUID(ctx interface{}, id interface{}) *DB_GetUserByUID_Call { - return &DB_GetUserByUID_Call{Call: _e.mock.On("GetUserByUID", ctx, id)} -} - -func (_c *DB_GetUserByUID_Call) Run(run func(ctx context.Context, id int32)) *DB_GetUserByUID_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(int32)) - }) - return _c -} - -func (_c *DB_GetUserByUID_Call) Return(_a0 database.User, _a1 error) *DB_GetUserByUID_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *DB_GetUserByUID_Call) RunAndReturn(run func(context.Context, int32) (database.User, error)) *DB_GetUserByUID_Call { - _c.Call.Return(run) - return _c -} - -// GetUserByUsername provides a mock function with given fields: ctx, username -func (_m *DB) GetUserByUsername(ctx context.Context, username string) (database.User, error) { - ret := _m.Called(ctx, username) - - if len(ret) == 0 { - panic("no return value specified for GetUserByUsername") - } - - var r0 database.User - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (database.User, error)); ok { - return rf(ctx, username) - } - if rf, ok := ret.Get(0).(func(context.Context, string) database.User); ok { - r0 = rf(ctx, username) - } else { - r0 = ret.Get(0).(database.User) - } - - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, username) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// DB_GetUserByUsername_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetUserByUsername' -type DB_GetUserByUsername_Call struct { - *mock.Call -} - -// GetUserByUsername is a helper method to define mock.On call -// - ctx context.Context -// - username string -func (_e *DB_Expecter) GetUserByUsername(ctx interface{}, username interface{}) *DB_GetUserByUsername_Call { - return &DB_GetUserByUsername_Call{Call: _e.mock.On("GetUserByUsername", ctx, username)} -} - -func (_c *DB_GetUserByUsername_Call) Run(run func(ctx context.Context, username string)) *DB_GetUserByUsername_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string)) - }) - return _c -} - -func (_c *DB_GetUserByUsername_Call) Return(_a0 database.User, _a1 error) *DB_GetUserByUsername_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *DB_GetUserByUsername_Call) RunAndReturn(run func(context.Context, string) (database.User, error)) *DB_GetUserByUsername_Call { - _c.Call.Return(run) - return _c -} - -// GetUsers provides a mock function with given fields: ctx -func (_m *DB) GetUsers(ctx context.Context) ([]database.User, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for GetUsers") - } - - var r0 []database.User - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) ([]database.User, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) []database.User); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]database.User) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// DB_GetUsers_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetUsers' -type DB_GetUsers_Call struct { - *mock.Call -} - -// GetUsers is a helper method to define mock.On call -// - ctx context.Context -func (_e *DB_Expecter) GetUsers(ctx interface{}) *DB_GetUsers_Call { - return &DB_GetUsers_Call{Call: _e.mock.On("GetUsers", ctx)} -} - -func (_c *DB_GetUsers_Call) Run(run func(ctx context.Context)) *DB_GetUsers_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *DB_GetUsers_Call) Return(_a0 []database.User, _a1 error) *DB_GetUsers_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *DB_GetUsers_Call) RunAndReturn(run func(context.Context) ([]database.User, error)) *DB_GetUsers_Call { - _c.Call.Return(run) - return _c -} - -// SetCallTranscript provides a mock function with given fields: ctx, iD, transcript -func (_m *DB) SetCallTranscript(ctx context.Context, iD uuid.UUID, transcript *string) error { - ret := _m.Called(ctx, iD, transcript) - - if len(ret) == 0 { - panic("no return value specified for SetCallTranscript") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, uuid.UUID, *string) error); ok { - r0 = rf(ctx, iD, transcript) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DB_SetCallTranscript_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetCallTranscript' -type DB_SetCallTranscript_Call struct { - *mock.Call -} - -// SetCallTranscript is a helper method to define mock.On call -// - ctx context.Context -// - iD uuid.UUID -// - transcript *string -func (_e *DB_Expecter) SetCallTranscript(ctx interface{}, iD interface{}, transcript interface{}) *DB_SetCallTranscript_Call { - return &DB_SetCallTranscript_Call{Call: _e.mock.On("SetCallTranscript", ctx, iD, transcript)} -} - -func (_c *DB_SetCallTranscript_Call) Run(run func(ctx context.Context, iD uuid.UUID, transcript *string)) *DB_SetCallTranscript_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(uuid.UUID), args[2].(*string)) - }) - return _c -} - -func (_c *DB_SetCallTranscript_Call) Return(_a0 error) *DB_SetCallTranscript_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *DB_SetCallTranscript_Call) RunAndReturn(run func(context.Context, uuid.UUID, *string) error) *DB_SetCallTranscript_Call { - _c.Call.Return(run) - return _c -} - -// SetTalkgroupTags provides a mock function with given fields: ctx, tags, systemID, tgID -func (_m *DB) SetTalkgroupTags(ctx context.Context, tags []string, systemID int32, tgID int32) error { - ret := _m.Called(ctx, tags, systemID, tgID) - - if len(ret) == 0 { - panic("no return value specified for SetTalkgroupTags") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, []string, int32, int32) error); ok { - r0 = rf(ctx, tags, systemID, tgID) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DB_SetTalkgroupTags_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetTalkgroupTags' -type DB_SetTalkgroupTags_Call struct { - *mock.Call -} - -// SetTalkgroupTags is a helper method to define mock.On call -// - ctx context.Context -// - tags []string -// - systemID int32 -// - tgID int32 -func (_e *DB_Expecter) SetTalkgroupTags(ctx interface{}, tags interface{}, systemID interface{}, tgID interface{}) *DB_SetTalkgroupTags_Call { - return &DB_SetTalkgroupTags_Call{Call: _e.mock.On("SetTalkgroupTags", ctx, tags, systemID, tgID)} -} - -func (_c *DB_SetTalkgroupTags_Call) Run(run func(ctx context.Context, tags []string, systemID int32, tgID int32)) *DB_SetTalkgroupTags_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].([]string), args[2].(int32), args[3].(int32)) - }) - return _c -} - -func (_c *DB_SetTalkgroupTags_Call) Return(_a0 error) *DB_SetTalkgroupTags_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *DB_SetTalkgroupTags_Call) RunAndReturn(run func(context.Context, []string, int32, int32) error) *DB_SetTalkgroupTags_Call { - _c.Call.Return(run) - return _c -} - -// UpdatePassword provides a mock function with given fields: ctx, username, password -func (_m *DB) UpdatePassword(ctx context.Context, username string, password string) error { - ret := _m.Called(ctx, username, password) - - if len(ret) == 0 { - panic("no return value specified for UpdatePassword") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { - r0 = rf(ctx, username, password) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DB_UpdatePassword_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdatePassword' -type DB_UpdatePassword_Call struct { - *mock.Call -} - -// UpdatePassword is a helper method to define mock.On call -// - ctx context.Context -// - username string -// - password string -func (_e *DB_Expecter) UpdatePassword(ctx interface{}, username interface{}, password interface{}) *DB_UpdatePassword_Call { - return &DB_UpdatePassword_Call{Call: _e.mock.On("UpdatePassword", ctx, username, password)} -} - -func (_c *DB_UpdatePassword_Call) Run(run func(ctx context.Context, username string, password string)) *DB_UpdatePassword_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(string)) - }) - return _c -} - -func (_c *DB_UpdatePassword_Call) Return(_a0 error) *DB_UpdatePassword_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *DB_UpdatePassword_Call) RunAndReturn(run func(context.Context, string, string) error) *DB_UpdatePassword_Call { - _c.Call.Return(run) - return _c -} - -// UpdateTalkgroup provides a mock function with given fields: ctx, arg -func (_m *DB) UpdateTalkgroup(ctx context.Context, arg database.UpdateTalkgroupParams) (database.Talkgroup, error) { - ret := _m.Called(ctx, arg) - - if len(ret) == 0 { - panic("no return value specified for UpdateTalkgroup") - } - - var r0 database.Talkgroup - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, database.UpdateTalkgroupParams) (database.Talkgroup, error)); ok { - return rf(ctx, arg) - } - if rf, ok := ret.Get(0).(func(context.Context, database.UpdateTalkgroupParams) database.Talkgroup); ok { - r0 = rf(ctx, arg) - } else { - r0 = ret.Get(0).(database.Talkgroup) - } - - if rf, ok := ret.Get(1).(func(context.Context, database.UpdateTalkgroupParams) error); ok { - r1 = rf(ctx, arg) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// DB_UpdateTalkgroup_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateTalkgroup' -type DB_UpdateTalkgroup_Call struct { - *mock.Call -} - -// UpdateTalkgroup is a helper method to define mock.On call -// - ctx context.Context -// - arg database.UpdateTalkgroupParams -func (_e *DB_Expecter) UpdateTalkgroup(ctx interface{}, arg interface{}) *DB_UpdateTalkgroup_Call { - return &DB_UpdateTalkgroup_Call{Call: _e.mock.On("UpdateTalkgroup", ctx, arg)} -} - -func (_c *DB_UpdateTalkgroup_Call) Run(run func(ctx context.Context, arg database.UpdateTalkgroupParams)) *DB_UpdateTalkgroup_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(database.UpdateTalkgroupParams)) - }) - return _c -} - -func (_c *DB_UpdateTalkgroup_Call) Return(_a0 database.Talkgroup, _a1 error) *DB_UpdateTalkgroup_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *DB_UpdateTalkgroup_Call) RunAndReturn(run func(context.Context, database.UpdateTalkgroupParams) (database.Talkgroup, error)) *DB_UpdateTalkgroup_Call { - _c.Call.Return(run) - return _c -} - -// NewDB creates a new instance of DB. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewDB(t interface { - mock.TestingT - Cleanup(func()) -}) *DB { - mock := &DB{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/pkg/database/mocks/Store.go b/pkg/database/mocks/Store.go new file mode 100644 index 0000000..5ea8209 --- /dev/null +++ b/pkg/database/mocks/Store.go @@ -0,0 +1,1786 @@ +// Code generated by mockery v2.47.0. DO NOT EDIT. + +package mocks + +import ( + context "context" + + database "dynatron.me/x/stillbox/pkg/database" + mock "github.com/stretchr/testify/mock" + + pgtype "github.com/jackc/pgx/v5/pgtype" + + pgx "github.com/jackc/pgx/v5" + + uuid "github.com/google/uuid" +) + +// Store is an autogenerated mock type for the Store type +type Store struct { + mock.Mock +} + +type Store_Expecter struct { + mock *mock.Mock +} + +func (_m *Store) EXPECT() *Store_Expecter { + return &Store_Expecter{mock: &_m.Mock} +} + +// AddAlert provides a mock function with given fields: ctx, arg +func (_m *Store) AddAlert(ctx context.Context, arg database.AddAlertParams) error { + ret := _m.Called(ctx, arg) + + if len(ret) == 0 { + panic("no return value specified for AddAlert") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, database.AddAlertParams) error); ok { + r0 = rf(ctx, arg) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Store_AddAlert_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddAlert' +type Store_AddAlert_Call struct { + *mock.Call +} + +// AddAlert is a helper method to define mock.On call +// - ctx context.Context +// - arg database.AddAlertParams +func (_e *Store_Expecter) AddAlert(ctx interface{}, arg interface{}) *Store_AddAlert_Call { + return &Store_AddAlert_Call{Call: _e.mock.On("AddAlert", ctx, arg)} +} + +func (_c *Store_AddAlert_Call) Run(run func(ctx context.Context, arg database.AddAlertParams)) *Store_AddAlert_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(database.AddAlertParams)) + }) + return _c +} + +func (_c *Store_AddAlert_Call) Return(_a0 error) *Store_AddAlert_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *Store_AddAlert_Call) RunAndReturn(run func(context.Context, database.AddAlertParams) error) *Store_AddAlert_Call { + _c.Call.Return(run) + return _c +} + +// AddCall provides a mock function with given fields: ctx, arg +func (_m *Store) AddCall(ctx context.Context, arg database.AddCallParams) error { + ret := _m.Called(ctx, arg) + + if len(ret) == 0 { + panic("no return value specified for AddCall") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, database.AddCallParams) error); ok { + r0 = rf(ctx, arg) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Store_AddCall_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddCall' +type Store_AddCall_Call struct { + *mock.Call +} + +// AddCall is a helper method to define mock.On call +// - ctx context.Context +// - arg database.AddCallParams +func (_e *Store_Expecter) AddCall(ctx interface{}, arg interface{}) *Store_AddCall_Call { + return &Store_AddCall_Call{Call: _e.mock.On("AddCall", ctx, arg)} +} + +func (_c *Store_AddCall_Call) Run(run func(ctx context.Context, arg database.AddCallParams)) *Store_AddCall_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(database.AddCallParams)) + }) + return _c +} + +func (_c *Store_AddCall_Call) Return(_a0 error) *Store_AddCall_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *Store_AddCall_Call) RunAndReturn(run func(context.Context, database.AddCallParams) error) *Store_AddCall_Call { + _c.Call.Return(run) + return _c +} + +// AddLearnedTalkgroup provides a mock function with given fields: ctx, arg +func (_m *Store) AddLearnedTalkgroup(ctx context.Context, arg database.AddLearnedTalkgroupParams) (int, error) { + ret := _m.Called(ctx, arg) + + if len(ret) == 0 { + panic("no return value specified for AddLearnedTalkgroup") + } + + var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, database.AddLearnedTalkgroupParams) (int, error)); ok { + return rf(ctx, arg) + } + if rf, ok := ret.Get(0).(func(context.Context, database.AddLearnedTalkgroupParams) int); ok { + r0 = rf(ctx, arg) + } else { + r0 = ret.Get(0).(int) + } + + if rf, ok := ret.Get(1).(func(context.Context, database.AddLearnedTalkgroupParams) error); ok { + r1 = rf(ctx, arg) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Store_AddLearnedTalkgroup_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddLearnedTalkgroup' +type Store_AddLearnedTalkgroup_Call struct { + *mock.Call +} + +// AddLearnedTalkgroup is a helper method to define mock.On call +// - ctx context.Context +// - arg database.AddLearnedTalkgroupParams +func (_e *Store_Expecter) AddLearnedTalkgroup(ctx interface{}, arg interface{}) *Store_AddLearnedTalkgroup_Call { + return &Store_AddLearnedTalkgroup_Call{Call: _e.mock.On("AddLearnedTalkgroup", ctx, arg)} +} + +func (_c *Store_AddLearnedTalkgroup_Call) Run(run func(ctx context.Context, arg database.AddLearnedTalkgroupParams)) *Store_AddLearnedTalkgroup_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(database.AddLearnedTalkgroupParams)) + }) + return _c +} + +func (_c *Store_AddLearnedTalkgroup_Call) Return(_a0 int, _a1 error) *Store_AddLearnedTalkgroup_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Store_AddLearnedTalkgroup_Call) RunAndReturn(run func(context.Context, database.AddLearnedTalkgroupParams) (int, error)) *Store_AddLearnedTalkgroup_Call { + _c.Call.Return(run) + return _c +} + +// AddTalkgroupWithLearnedFlag provides a mock function with given fields: ctx, systemID, tGID +func (_m *Store) AddTalkgroupWithLearnedFlag(ctx context.Context, systemID int32, tGID int32) error { + ret := _m.Called(ctx, systemID, tGID) + + if len(ret) == 0 { + panic("no return value specified for AddTalkgroupWithLearnedFlag") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, int32, int32) error); ok { + r0 = rf(ctx, systemID, tGID) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Store_AddTalkgroupWithLearnedFlag_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddTalkgroupWithLearnedFlag' +type Store_AddTalkgroupWithLearnedFlag_Call struct { + *mock.Call +} + +// AddTalkgroupWithLearnedFlag is a helper method to define mock.On call +// - ctx context.Context +// - systemID int32 +// - tGID int32 +func (_e *Store_Expecter) AddTalkgroupWithLearnedFlag(ctx interface{}, systemID interface{}, tGID interface{}) *Store_AddTalkgroupWithLearnedFlag_Call { + return &Store_AddTalkgroupWithLearnedFlag_Call{Call: _e.mock.On("AddTalkgroupWithLearnedFlag", ctx, systemID, tGID)} +} + +func (_c *Store_AddTalkgroupWithLearnedFlag_Call) Run(run func(ctx context.Context, systemID int32, tGID int32)) *Store_AddTalkgroupWithLearnedFlag_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(int32), args[2].(int32)) + }) + return _c +} + +func (_c *Store_AddTalkgroupWithLearnedFlag_Call) Return(_a0 error) *Store_AddTalkgroupWithLearnedFlag_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *Store_AddTalkgroupWithLearnedFlag_Call) RunAndReturn(run func(context.Context, int32, int32) error) *Store_AddTalkgroupWithLearnedFlag_Call { + _c.Call.Return(run) + return _c +} + +// BulkSetTalkgroupTags provides a mock function with given fields: ctx, tgs, tags +func (_m *Store) BulkSetTalkgroupTags(ctx context.Context, tgs database.TGTuples, tags []string) error { + ret := _m.Called(ctx, tgs, tags) + + if len(ret) == 0 { + panic("no return value specified for BulkSetTalkgroupTags") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, database.TGTuples, []string) error); ok { + r0 = rf(ctx, tgs, tags) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Store_BulkSetTalkgroupTags_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BulkSetTalkgroupTags' +type Store_BulkSetTalkgroupTags_Call struct { + *mock.Call +} + +// BulkSetTalkgroupTags is a helper method to define mock.On call +// - ctx context.Context +// - tgs database.TGTuples +// - tags []string +func (_e *Store_Expecter) BulkSetTalkgroupTags(ctx interface{}, tgs interface{}, tags interface{}) *Store_BulkSetTalkgroupTags_Call { + return &Store_BulkSetTalkgroupTags_Call{Call: _e.mock.On("BulkSetTalkgroupTags", ctx, tgs, tags)} +} + +func (_c *Store_BulkSetTalkgroupTags_Call) Run(run func(ctx context.Context, tgs database.TGTuples, tags []string)) *Store_BulkSetTalkgroupTags_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(database.TGTuples), args[2].([]string)) + }) + return _c +} + +func (_c *Store_BulkSetTalkgroupTags_Call) Return(_a0 error) *Store_BulkSetTalkgroupTags_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *Store_BulkSetTalkgroupTags_Call) RunAndReturn(run func(context.Context, database.TGTuples, []string) error) *Store_BulkSetTalkgroupTags_Call { + _c.Call.Return(run) + return _c +} + +// CreateAPIKey provides a mock function with given fields: ctx, owner, expires, disabled +func (_m *Store) CreateAPIKey(ctx context.Context, owner int, expires pgtype.Timestamp, disabled *bool) (database.ApiKey, error) { + ret := _m.Called(ctx, owner, expires, disabled) + + if len(ret) == 0 { + panic("no return value specified for CreateAPIKey") + } + + var r0 database.ApiKey + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int, pgtype.Timestamp, *bool) (database.ApiKey, error)); ok { + return rf(ctx, owner, expires, disabled) + } + if rf, ok := ret.Get(0).(func(context.Context, int, pgtype.Timestamp, *bool) database.ApiKey); ok { + r0 = rf(ctx, owner, expires, disabled) + } else { + r0 = ret.Get(0).(database.ApiKey) + } + + if rf, ok := ret.Get(1).(func(context.Context, int, pgtype.Timestamp, *bool) error); ok { + r1 = rf(ctx, owner, expires, disabled) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Store_CreateAPIKey_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateAPIKey' +type Store_CreateAPIKey_Call struct { + *mock.Call +} + +// CreateAPIKey is a helper method to define mock.On call +// - ctx context.Context +// - owner int +// - expires pgtype.Timestamp +// - disabled *bool +func (_e *Store_Expecter) CreateAPIKey(ctx interface{}, owner interface{}, expires interface{}, disabled interface{}) *Store_CreateAPIKey_Call { + return &Store_CreateAPIKey_Call{Call: _e.mock.On("CreateAPIKey", ctx, owner, expires, disabled)} +} + +func (_c *Store_CreateAPIKey_Call) Run(run func(ctx context.Context, owner int, expires pgtype.Timestamp, disabled *bool)) *Store_CreateAPIKey_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(int), args[2].(pgtype.Timestamp), args[3].(*bool)) + }) + return _c +} + +func (_c *Store_CreateAPIKey_Call) Return(_a0 database.ApiKey, _a1 error) *Store_CreateAPIKey_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Store_CreateAPIKey_Call) RunAndReturn(run func(context.Context, int, pgtype.Timestamp, *bool) (database.ApiKey, error)) *Store_CreateAPIKey_Call { + _c.Call.Return(run) + return _c +} + +// CreateUser provides a mock function with given fields: ctx, arg +func (_m *Store) CreateUser(ctx context.Context, arg database.CreateUserParams) (database.User, error) { + ret := _m.Called(ctx, arg) + + if len(ret) == 0 { + panic("no return value specified for CreateUser") + } + + var r0 database.User + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, database.CreateUserParams) (database.User, error)); ok { + return rf(ctx, arg) + } + if rf, ok := ret.Get(0).(func(context.Context, database.CreateUserParams) database.User); ok { + r0 = rf(ctx, arg) + } else { + r0 = ret.Get(0).(database.User) + } + + if rf, ok := ret.Get(1).(func(context.Context, database.CreateUserParams) error); ok { + r1 = rf(ctx, arg) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Store_CreateUser_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateUser' +type Store_CreateUser_Call struct { + *mock.Call +} + +// CreateUser is a helper method to define mock.On call +// - ctx context.Context +// - arg database.CreateUserParams +func (_e *Store_Expecter) CreateUser(ctx interface{}, arg interface{}) *Store_CreateUser_Call { + return &Store_CreateUser_Call{Call: _e.mock.On("CreateUser", ctx, arg)} +} + +func (_c *Store_CreateUser_Call) Run(run func(ctx context.Context, arg database.CreateUserParams)) *Store_CreateUser_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(database.CreateUserParams)) + }) + return _c +} + +func (_c *Store_CreateUser_Call) Return(_a0 database.User, _a1 error) *Store_CreateUser_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Store_CreateUser_Call) RunAndReturn(run func(context.Context, database.CreateUserParams) (database.User, error)) *Store_CreateUser_Call { + _c.Call.Return(run) + return _c +} + +// DB provides a mock function with given fields: +func (_m *Store) DB() *database.Database { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for DB") + } + + var r0 *database.Database + if rf, ok := ret.Get(0).(func() *database.Database); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*database.Database) + } + } + + return r0 +} + +// Store_DB_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DB' +type Store_DB_Call struct { + *mock.Call +} + +// DB is a helper method to define mock.On call +func (_e *Store_Expecter) DB() *Store_DB_Call { + return &Store_DB_Call{Call: _e.mock.On("DB")} +} + +func (_c *Store_DB_Call) Run(run func()) *Store_DB_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *Store_DB_Call) Return(_a0 *database.Database) *Store_DB_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *Store_DB_Call) RunAndReturn(run func() *database.Database) *Store_DB_Call { + _c.Call.Return(run) + return _c +} + +// DeleteAPIKey provides a mock function with given fields: ctx, apiKey +func (_m *Store) DeleteAPIKey(ctx context.Context, apiKey string) error { + ret := _m.Called(ctx, apiKey) + + if len(ret) == 0 { + panic("no return value specified for DeleteAPIKey") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { + r0 = rf(ctx, apiKey) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Store_DeleteAPIKey_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteAPIKey' +type Store_DeleteAPIKey_Call struct { + *mock.Call +} + +// DeleteAPIKey is a helper method to define mock.On call +// - ctx context.Context +// - apiKey string +func (_e *Store_Expecter) DeleteAPIKey(ctx interface{}, apiKey interface{}) *Store_DeleteAPIKey_Call { + return &Store_DeleteAPIKey_Call{Call: _e.mock.On("DeleteAPIKey", ctx, apiKey)} +} + +func (_c *Store_DeleteAPIKey_Call) Run(run func(ctx context.Context, apiKey string)) *Store_DeleteAPIKey_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string)) + }) + return _c +} + +func (_c *Store_DeleteAPIKey_Call) Return(_a0 error) *Store_DeleteAPIKey_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *Store_DeleteAPIKey_Call) RunAndReturn(run func(context.Context, string) error) *Store_DeleteAPIKey_Call { + _c.Call.Return(run) + return _c +} + +// DeleteUser provides a mock function with given fields: ctx, username +func (_m *Store) DeleteUser(ctx context.Context, username string) error { + ret := _m.Called(ctx, username) + + if len(ret) == 0 { + panic("no return value specified for DeleteUser") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { + r0 = rf(ctx, username) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Store_DeleteUser_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteUser' +type Store_DeleteUser_Call struct { + *mock.Call +} + +// DeleteUser is a helper method to define mock.On call +// - ctx context.Context +// - username string +func (_e *Store_Expecter) DeleteUser(ctx interface{}, username interface{}) *Store_DeleteUser_Call { + return &Store_DeleteUser_Call{Call: _e.mock.On("DeleteUser", ctx, username)} +} + +func (_c *Store_DeleteUser_Call) Run(run func(ctx context.Context, username string)) *Store_DeleteUser_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string)) + }) + return _c +} + +func (_c *Store_DeleteUser_Call) Return(_a0 error) *Store_DeleteUser_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *Store_DeleteUser_Call) RunAndReturn(run func(context.Context, string) error) *Store_DeleteUser_Call { + _c.Call.Return(run) + return _c +} + +// GetAPIKey provides a mock function with given fields: ctx, apiKey +func (_m *Store) GetAPIKey(ctx context.Context, apiKey string) (database.ApiKey, error) { + ret := _m.Called(ctx, apiKey) + + if len(ret) == 0 { + panic("no return value specified for GetAPIKey") + } + + var r0 database.ApiKey + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (database.ApiKey, error)); ok { + return rf(ctx, apiKey) + } + if rf, ok := ret.Get(0).(func(context.Context, string) database.ApiKey); ok { + r0 = rf(ctx, apiKey) + } else { + r0 = ret.Get(0).(database.ApiKey) + } + + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, apiKey) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Store_GetAPIKey_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetAPIKey' +type Store_GetAPIKey_Call struct { + *mock.Call +} + +// GetAPIKey is a helper method to define mock.On call +// - ctx context.Context +// - apiKey string +func (_e *Store_Expecter) GetAPIKey(ctx interface{}, apiKey interface{}) *Store_GetAPIKey_Call { + return &Store_GetAPIKey_Call{Call: _e.mock.On("GetAPIKey", ctx, apiKey)} +} + +func (_c *Store_GetAPIKey_Call) Run(run func(ctx context.Context, apiKey string)) *Store_GetAPIKey_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string)) + }) + return _c +} + +func (_c *Store_GetAPIKey_Call) Return(_a0 database.ApiKey, _a1 error) *Store_GetAPIKey_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Store_GetAPIKey_Call) RunAndReturn(run func(context.Context, string) (database.ApiKey, error)) *Store_GetAPIKey_Call { + _c.Call.Return(run) + return _c +} + +// GetDatabaseSize provides a mock function with given fields: ctx +func (_m *Store) GetDatabaseSize(ctx context.Context) (string, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for GetDatabaseSize") + } + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (string, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) string); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Store_GetDatabaseSize_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetDatabaseSize' +type Store_GetDatabaseSize_Call struct { + *mock.Call +} + +// GetDatabaseSize is a helper method to define mock.On call +// - ctx context.Context +func (_e *Store_Expecter) GetDatabaseSize(ctx interface{}) *Store_GetDatabaseSize_Call { + return &Store_GetDatabaseSize_Call{Call: _e.mock.On("GetDatabaseSize", ctx)} +} + +func (_c *Store_GetDatabaseSize_Call) Run(run func(ctx context.Context)) *Store_GetDatabaseSize_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *Store_GetDatabaseSize_Call) Return(_a0 string, _a1 error) *Store_GetDatabaseSize_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Store_GetDatabaseSize_Call) RunAndReturn(run func(context.Context) (string, error)) *Store_GetDatabaseSize_Call { + _c.Call.Return(run) + return _c +} + +// GetSystemName provides a mock function with given fields: ctx, systemID +func (_m *Store) GetSystemName(ctx context.Context, systemID int) (string, error) { + ret := _m.Called(ctx, systemID) + + if len(ret) == 0 { + panic("no return value specified for GetSystemName") + } + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (string, error)); ok { + return rf(ctx, systemID) + } + if rf, ok := ret.Get(0).(func(context.Context, int) string); ok { + r0 = rf(ctx, systemID) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { + r1 = rf(ctx, systemID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Store_GetSystemName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetSystemName' +type Store_GetSystemName_Call struct { + *mock.Call +} + +// GetSystemName is a helper method to define mock.On call +// - ctx context.Context +// - systemID int +func (_e *Store_Expecter) GetSystemName(ctx interface{}, systemID interface{}) *Store_GetSystemName_Call { + return &Store_GetSystemName_Call{Call: _e.mock.On("GetSystemName", ctx, systemID)} +} + +func (_c *Store_GetSystemName_Call) Run(run func(ctx context.Context, systemID int)) *Store_GetSystemName_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(int)) + }) + return _c +} + +func (_c *Store_GetSystemName_Call) Return(_a0 string, _a1 error) *Store_GetSystemName_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Store_GetSystemName_Call) RunAndReturn(run func(context.Context, int) (string, error)) *Store_GetSystemName_Call { + _c.Call.Return(run) + return _c +} + +// GetTalkgroup provides a mock function with given fields: ctx, systemID, tGID +func (_m *Store) GetTalkgroup(ctx context.Context, systemID int32, tGID int32) (database.GetTalkgroupRow, error) { + ret := _m.Called(ctx, systemID, tGID) + + if len(ret) == 0 { + panic("no return value specified for GetTalkgroup") + } + + var r0 database.GetTalkgroupRow + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int32, int32) (database.GetTalkgroupRow, error)); ok { + return rf(ctx, systemID, tGID) + } + if rf, ok := ret.Get(0).(func(context.Context, int32, int32) database.GetTalkgroupRow); ok { + r0 = rf(ctx, systemID, tGID) + } else { + r0 = ret.Get(0).(database.GetTalkgroupRow) + } + + if rf, ok := ret.Get(1).(func(context.Context, int32, int32) error); ok { + r1 = rf(ctx, systemID, tGID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Store_GetTalkgroup_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTalkgroup' +type Store_GetTalkgroup_Call struct { + *mock.Call +} + +// GetTalkgroup is a helper method to define mock.On call +// - ctx context.Context +// - systemID int32 +// - tGID int32 +func (_e *Store_Expecter) GetTalkgroup(ctx interface{}, systemID interface{}, tGID interface{}) *Store_GetTalkgroup_Call { + return &Store_GetTalkgroup_Call{Call: _e.mock.On("GetTalkgroup", ctx, systemID, tGID)} +} + +func (_c *Store_GetTalkgroup_Call) Run(run func(ctx context.Context, systemID int32, tGID int32)) *Store_GetTalkgroup_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(int32), args[2].(int32)) + }) + return _c +} + +func (_c *Store_GetTalkgroup_Call) Return(_a0 database.GetTalkgroupRow, _a1 error) *Store_GetTalkgroup_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Store_GetTalkgroup_Call) RunAndReturn(run func(context.Context, int32, int32) (database.GetTalkgroupRow, error)) *Store_GetTalkgroup_Call { + _c.Call.Return(run) + return _c +} + +// GetTalkgroupIDsByTags provides a mock function with given fields: ctx, anyTags, allTags, notTags +func (_m *Store) GetTalkgroupIDsByTags(ctx context.Context, anyTags []string, allTags []string, notTags []string) ([]database.GetTalkgroupIDsByTagsRow, error) { + ret := _m.Called(ctx, anyTags, allTags, notTags) + + if len(ret) == 0 { + panic("no return value specified for GetTalkgroupIDsByTags") + } + + var r0 []database.GetTalkgroupIDsByTagsRow + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []string, []string, []string) ([]database.GetTalkgroupIDsByTagsRow, error)); ok { + return rf(ctx, anyTags, allTags, notTags) + } + if rf, ok := ret.Get(0).(func(context.Context, []string, []string, []string) []database.GetTalkgroupIDsByTagsRow); ok { + r0 = rf(ctx, anyTags, allTags, notTags) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]database.GetTalkgroupIDsByTagsRow) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, []string, []string, []string) error); ok { + r1 = rf(ctx, anyTags, allTags, notTags) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Store_GetTalkgroupIDsByTags_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTalkgroupIDsByTags' +type Store_GetTalkgroupIDsByTags_Call struct { + *mock.Call +} + +// GetTalkgroupIDsByTags is a helper method to define mock.On call +// - ctx context.Context +// - anyTags []string +// - allTags []string +// - notTags []string +func (_e *Store_Expecter) GetTalkgroupIDsByTags(ctx interface{}, anyTags interface{}, allTags interface{}, notTags interface{}) *Store_GetTalkgroupIDsByTags_Call { + return &Store_GetTalkgroupIDsByTags_Call{Call: _e.mock.On("GetTalkgroupIDsByTags", ctx, anyTags, allTags, notTags)} +} + +func (_c *Store_GetTalkgroupIDsByTags_Call) Run(run func(ctx context.Context, anyTags []string, allTags []string, notTags []string)) *Store_GetTalkgroupIDsByTags_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].([]string), args[2].([]string), args[3].([]string)) + }) + return _c +} + +func (_c *Store_GetTalkgroupIDsByTags_Call) Return(_a0 []database.GetTalkgroupIDsByTagsRow, _a1 error) *Store_GetTalkgroupIDsByTags_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Store_GetTalkgroupIDsByTags_Call) RunAndReturn(run func(context.Context, []string, []string, []string) ([]database.GetTalkgroupIDsByTagsRow, error)) *Store_GetTalkgroupIDsByTags_Call { + _c.Call.Return(run) + return _c +} + +// GetTalkgroupTags provides a mock function with given fields: ctx, systemID, tGID +func (_m *Store) GetTalkgroupTags(ctx context.Context, systemID int32, tGID int32) ([]string, error) { + ret := _m.Called(ctx, systemID, tGID) + + if len(ret) == 0 { + panic("no return value specified for GetTalkgroupTags") + } + + var r0 []string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int32, int32) ([]string, error)); ok { + return rf(ctx, systemID, tGID) + } + if rf, ok := ret.Get(0).(func(context.Context, int32, int32) []string); ok { + r0 = rf(ctx, systemID, tGID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]string) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, int32, int32) error); ok { + r1 = rf(ctx, systemID, tGID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Store_GetTalkgroupTags_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTalkgroupTags' +type Store_GetTalkgroupTags_Call struct { + *mock.Call +} + +// GetTalkgroupTags is a helper method to define mock.On call +// - ctx context.Context +// - systemID int32 +// - tGID int32 +func (_e *Store_Expecter) GetTalkgroupTags(ctx interface{}, systemID interface{}, tGID interface{}) *Store_GetTalkgroupTags_Call { + return &Store_GetTalkgroupTags_Call{Call: _e.mock.On("GetTalkgroupTags", ctx, systemID, tGID)} +} + +func (_c *Store_GetTalkgroupTags_Call) Run(run func(ctx context.Context, systemID int32, tGID int32)) *Store_GetTalkgroupTags_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(int32), args[2].(int32)) + }) + return _c +} + +func (_c *Store_GetTalkgroupTags_Call) Return(_a0 []string, _a1 error) *Store_GetTalkgroupTags_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Store_GetTalkgroupTags_Call) RunAndReturn(run func(context.Context, int32, int32) ([]string, error)) *Store_GetTalkgroupTags_Call { + _c.Call.Return(run) + return _c +} + +// GetTalkgroupWithLearned provides a mock function with given fields: ctx, systemID, tGID +func (_m *Store) GetTalkgroupWithLearned(ctx context.Context, systemID int32, tGID int32) (database.GetTalkgroupWithLearnedRow, error) { + ret := _m.Called(ctx, systemID, tGID) + + if len(ret) == 0 { + panic("no return value specified for GetTalkgroupWithLearned") + } + + var r0 database.GetTalkgroupWithLearnedRow + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int32, int32) (database.GetTalkgroupWithLearnedRow, error)); ok { + return rf(ctx, systemID, tGID) + } + if rf, ok := ret.Get(0).(func(context.Context, int32, int32) database.GetTalkgroupWithLearnedRow); ok { + r0 = rf(ctx, systemID, tGID) + } else { + r0 = ret.Get(0).(database.GetTalkgroupWithLearnedRow) + } + + if rf, ok := ret.Get(1).(func(context.Context, int32, int32) error); ok { + r1 = rf(ctx, systemID, tGID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Store_GetTalkgroupWithLearned_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTalkgroupWithLearned' +type Store_GetTalkgroupWithLearned_Call struct { + *mock.Call +} + +// GetTalkgroupWithLearned is a helper method to define mock.On call +// - ctx context.Context +// - systemID int32 +// - tGID int32 +func (_e *Store_Expecter) GetTalkgroupWithLearned(ctx interface{}, systemID interface{}, tGID interface{}) *Store_GetTalkgroupWithLearned_Call { + return &Store_GetTalkgroupWithLearned_Call{Call: _e.mock.On("GetTalkgroupWithLearned", ctx, systemID, tGID)} +} + +func (_c *Store_GetTalkgroupWithLearned_Call) Run(run func(ctx context.Context, systemID int32, tGID int32)) *Store_GetTalkgroupWithLearned_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(int32), args[2].(int32)) + }) + return _c +} + +func (_c *Store_GetTalkgroupWithLearned_Call) Return(_a0 database.GetTalkgroupWithLearnedRow, _a1 error) *Store_GetTalkgroupWithLearned_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Store_GetTalkgroupWithLearned_Call) RunAndReturn(run func(context.Context, int32, int32) (database.GetTalkgroupWithLearnedRow, error)) *Store_GetTalkgroupWithLearned_Call { + _c.Call.Return(run) + return _c +} + +// GetTalkgroupsBySysTGID provides a mock function with given fields: ctx, ids +func (_m *Store) GetTalkgroupsBySysTGID(ctx context.Context, ids database.TGTuples) ([]database.GetTalkgroupsRow, error) { + ret := _m.Called(ctx, ids) + + if len(ret) == 0 { + panic("no return value specified for GetTalkgroupsBySysTGID") + } + + var r0 []database.GetTalkgroupsRow + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, database.TGTuples) ([]database.GetTalkgroupsRow, error)); ok { + return rf(ctx, ids) + } + if rf, ok := ret.Get(0).(func(context.Context, database.TGTuples) []database.GetTalkgroupsRow); ok { + r0 = rf(ctx, ids) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]database.GetTalkgroupsRow) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, database.TGTuples) error); ok { + r1 = rf(ctx, ids) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Store_GetTalkgroupsBySysTGID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTalkgroupsBySysTGID' +type Store_GetTalkgroupsBySysTGID_Call struct { + *mock.Call +} + +// GetTalkgroupsBySysTGID is a helper method to define mock.On call +// - ctx context.Context +// - ids database.TGTuples +func (_e *Store_Expecter) GetTalkgroupsBySysTGID(ctx interface{}, ids interface{}) *Store_GetTalkgroupsBySysTGID_Call { + return &Store_GetTalkgroupsBySysTGID_Call{Call: _e.mock.On("GetTalkgroupsBySysTGID", ctx, ids)} +} + +func (_c *Store_GetTalkgroupsBySysTGID_Call) Run(run func(ctx context.Context, ids database.TGTuples)) *Store_GetTalkgroupsBySysTGID_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(database.TGTuples)) + }) + return _c +} + +func (_c *Store_GetTalkgroupsBySysTGID_Call) Return(_a0 []database.GetTalkgroupsRow, _a1 error) *Store_GetTalkgroupsBySysTGID_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Store_GetTalkgroupsBySysTGID_Call) RunAndReturn(run func(context.Context, database.TGTuples) ([]database.GetTalkgroupsRow, error)) *Store_GetTalkgroupsBySysTGID_Call { + _c.Call.Return(run) + return _c +} + +// GetTalkgroupsWithAllTags provides a mock function with given fields: ctx, tags +func (_m *Store) GetTalkgroupsWithAllTags(ctx context.Context, tags []string) ([]database.GetTalkgroupsWithAllTagsRow, error) { + ret := _m.Called(ctx, tags) + + if len(ret) == 0 { + panic("no return value specified for GetTalkgroupsWithAllTags") + } + + var r0 []database.GetTalkgroupsWithAllTagsRow + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []string) ([]database.GetTalkgroupsWithAllTagsRow, error)); ok { + return rf(ctx, tags) + } + if rf, ok := ret.Get(0).(func(context.Context, []string) []database.GetTalkgroupsWithAllTagsRow); ok { + r0 = rf(ctx, tags) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]database.GetTalkgroupsWithAllTagsRow) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, []string) error); ok { + r1 = rf(ctx, tags) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Store_GetTalkgroupsWithAllTags_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTalkgroupsWithAllTags' +type Store_GetTalkgroupsWithAllTags_Call struct { + *mock.Call +} + +// GetTalkgroupsWithAllTags is a helper method to define mock.On call +// - ctx context.Context +// - tags []string +func (_e *Store_Expecter) GetTalkgroupsWithAllTags(ctx interface{}, tags interface{}) *Store_GetTalkgroupsWithAllTags_Call { + return &Store_GetTalkgroupsWithAllTags_Call{Call: _e.mock.On("GetTalkgroupsWithAllTags", ctx, tags)} +} + +func (_c *Store_GetTalkgroupsWithAllTags_Call) Run(run func(ctx context.Context, tags []string)) *Store_GetTalkgroupsWithAllTags_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].([]string)) + }) + return _c +} + +func (_c *Store_GetTalkgroupsWithAllTags_Call) Return(_a0 []database.GetTalkgroupsWithAllTagsRow, _a1 error) *Store_GetTalkgroupsWithAllTags_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Store_GetTalkgroupsWithAllTags_Call) RunAndReturn(run func(context.Context, []string) ([]database.GetTalkgroupsWithAllTagsRow, error)) *Store_GetTalkgroupsWithAllTags_Call { + _c.Call.Return(run) + return _c +} + +// GetTalkgroupsWithAnyTags provides a mock function with given fields: ctx, tags +func (_m *Store) GetTalkgroupsWithAnyTags(ctx context.Context, tags []string) ([]database.GetTalkgroupsWithAnyTagsRow, error) { + ret := _m.Called(ctx, tags) + + if len(ret) == 0 { + panic("no return value specified for GetTalkgroupsWithAnyTags") + } + + var r0 []database.GetTalkgroupsWithAnyTagsRow + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []string) ([]database.GetTalkgroupsWithAnyTagsRow, error)); ok { + return rf(ctx, tags) + } + if rf, ok := ret.Get(0).(func(context.Context, []string) []database.GetTalkgroupsWithAnyTagsRow); ok { + r0 = rf(ctx, tags) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]database.GetTalkgroupsWithAnyTagsRow) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, []string) error); ok { + r1 = rf(ctx, tags) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Store_GetTalkgroupsWithAnyTags_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTalkgroupsWithAnyTags' +type Store_GetTalkgroupsWithAnyTags_Call struct { + *mock.Call +} + +// GetTalkgroupsWithAnyTags is a helper method to define mock.On call +// - ctx context.Context +// - tags []string +func (_e *Store_Expecter) GetTalkgroupsWithAnyTags(ctx interface{}, tags interface{}) *Store_GetTalkgroupsWithAnyTags_Call { + return &Store_GetTalkgroupsWithAnyTags_Call{Call: _e.mock.On("GetTalkgroupsWithAnyTags", ctx, tags)} +} + +func (_c *Store_GetTalkgroupsWithAnyTags_Call) Run(run func(ctx context.Context, tags []string)) *Store_GetTalkgroupsWithAnyTags_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].([]string)) + }) + return _c +} + +func (_c *Store_GetTalkgroupsWithAnyTags_Call) Return(_a0 []database.GetTalkgroupsWithAnyTagsRow, _a1 error) *Store_GetTalkgroupsWithAnyTags_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Store_GetTalkgroupsWithAnyTags_Call) RunAndReturn(run func(context.Context, []string) ([]database.GetTalkgroupsWithAnyTagsRow, error)) *Store_GetTalkgroupsWithAnyTags_Call { + _c.Call.Return(run) + return _c +} + +// GetTalkgroupsWithLearned provides a mock function with given fields: ctx +func (_m *Store) GetTalkgroupsWithLearned(ctx context.Context) ([]database.GetTalkgroupsWithLearnedRow, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for GetTalkgroupsWithLearned") + } + + var r0 []database.GetTalkgroupsWithLearnedRow + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) ([]database.GetTalkgroupsWithLearnedRow, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) []database.GetTalkgroupsWithLearnedRow); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]database.GetTalkgroupsWithLearnedRow) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Store_GetTalkgroupsWithLearned_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTalkgroupsWithLearned' +type Store_GetTalkgroupsWithLearned_Call struct { + *mock.Call +} + +// GetTalkgroupsWithLearned is a helper method to define mock.On call +// - ctx context.Context +func (_e *Store_Expecter) GetTalkgroupsWithLearned(ctx interface{}) *Store_GetTalkgroupsWithLearned_Call { + return &Store_GetTalkgroupsWithLearned_Call{Call: _e.mock.On("GetTalkgroupsWithLearned", ctx)} +} + +func (_c *Store_GetTalkgroupsWithLearned_Call) Run(run func(ctx context.Context)) *Store_GetTalkgroupsWithLearned_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *Store_GetTalkgroupsWithLearned_Call) Return(_a0 []database.GetTalkgroupsWithLearnedRow, _a1 error) *Store_GetTalkgroupsWithLearned_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Store_GetTalkgroupsWithLearned_Call) RunAndReturn(run func(context.Context) ([]database.GetTalkgroupsWithLearnedRow, error)) *Store_GetTalkgroupsWithLearned_Call { + _c.Call.Return(run) + return _c +} + +// GetTalkgroupsWithLearnedBySysTGID provides a mock function with given fields: ctx, ids +func (_m *Store) GetTalkgroupsWithLearnedBySysTGID(ctx context.Context, ids database.TGTuples) ([]database.GetTalkgroupsRow, error) { + ret := _m.Called(ctx, ids) + + if len(ret) == 0 { + panic("no return value specified for GetTalkgroupsWithLearnedBySysTGID") + } + + var r0 []database.GetTalkgroupsRow + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, database.TGTuples) ([]database.GetTalkgroupsRow, error)); ok { + return rf(ctx, ids) + } + if rf, ok := ret.Get(0).(func(context.Context, database.TGTuples) []database.GetTalkgroupsRow); ok { + r0 = rf(ctx, ids) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]database.GetTalkgroupsRow) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, database.TGTuples) error); ok { + r1 = rf(ctx, ids) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Store_GetTalkgroupsWithLearnedBySysTGID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTalkgroupsWithLearnedBySysTGID' +type Store_GetTalkgroupsWithLearnedBySysTGID_Call struct { + *mock.Call +} + +// GetTalkgroupsWithLearnedBySysTGID is a helper method to define mock.On call +// - ctx context.Context +// - ids database.TGTuples +func (_e *Store_Expecter) GetTalkgroupsWithLearnedBySysTGID(ctx interface{}, ids interface{}) *Store_GetTalkgroupsWithLearnedBySysTGID_Call { + return &Store_GetTalkgroupsWithLearnedBySysTGID_Call{Call: _e.mock.On("GetTalkgroupsWithLearnedBySysTGID", ctx, ids)} +} + +func (_c *Store_GetTalkgroupsWithLearnedBySysTGID_Call) Run(run func(ctx context.Context, ids database.TGTuples)) *Store_GetTalkgroupsWithLearnedBySysTGID_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(database.TGTuples)) + }) + return _c +} + +func (_c *Store_GetTalkgroupsWithLearnedBySysTGID_Call) Return(_a0 []database.GetTalkgroupsRow, _a1 error) *Store_GetTalkgroupsWithLearnedBySysTGID_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Store_GetTalkgroupsWithLearnedBySysTGID_Call) RunAndReturn(run func(context.Context, database.TGTuples) ([]database.GetTalkgroupsRow, error)) *Store_GetTalkgroupsWithLearnedBySysTGID_Call { + _c.Call.Return(run) + return _c +} + +// GetTalkgroupsWithLearnedBySystem provides a mock function with given fields: ctx, system +func (_m *Store) GetTalkgroupsWithLearnedBySystem(ctx context.Context, system int32) ([]database.GetTalkgroupsWithLearnedBySystemRow, error) { + ret := _m.Called(ctx, system) + + if len(ret) == 0 { + panic("no return value specified for GetTalkgroupsWithLearnedBySystem") + } + + var r0 []database.GetTalkgroupsWithLearnedBySystemRow + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int32) ([]database.GetTalkgroupsWithLearnedBySystemRow, error)); ok { + return rf(ctx, system) + } + if rf, ok := ret.Get(0).(func(context.Context, int32) []database.GetTalkgroupsWithLearnedBySystemRow); ok { + r0 = rf(ctx, system) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]database.GetTalkgroupsWithLearnedBySystemRow) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, int32) error); ok { + r1 = rf(ctx, system) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Store_GetTalkgroupsWithLearnedBySystem_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTalkgroupsWithLearnedBySystem' +type Store_GetTalkgroupsWithLearnedBySystem_Call struct { + *mock.Call +} + +// GetTalkgroupsWithLearnedBySystem is a helper method to define mock.On call +// - ctx context.Context +// - system int32 +func (_e *Store_Expecter) GetTalkgroupsWithLearnedBySystem(ctx interface{}, system interface{}) *Store_GetTalkgroupsWithLearnedBySystem_Call { + return &Store_GetTalkgroupsWithLearnedBySystem_Call{Call: _e.mock.On("GetTalkgroupsWithLearnedBySystem", ctx, system)} +} + +func (_c *Store_GetTalkgroupsWithLearnedBySystem_Call) Run(run func(ctx context.Context, system int32)) *Store_GetTalkgroupsWithLearnedBySystem_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(int32)) + }) + return _c +} + +func (_c *Store_GetTalkgroupsWithLearnedBySystem_Call) Return(_a0 []database.GetTalkgroupsWithLearnedBySystemRow, _a1 error) *Store_GetTalkgroupsWithLearnedBySystem_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Store_GetTalkgroupsWithLearnedBySystem_Call) RunAndReturn(run func(context.Context, int32) ([]database.GetTalkgroupsWithLearnedBySystemRow, error)) *Store_GetTalkgroupsWithLearnedBySystem_Call { + _c.Call.Return(run) + return _c +} + +// GetUserByID provides a mock function with given fields: ctx, id +func (_m *Store) GetUserByID(ctx context.Context, id int32) (database.User, error) { + ret := _m.Called(ctx, id) + + if len(ret) == 0 { + panic("no return value specified for GetUserByID") + } + + var r0 database.User + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int32) (database.User, error)); ok { + return rf(ctx, id) + } + if rf, ok := ret.Get(0).(func(context.Context, int32) database.User); ok { + r0 = rf(ctx, id) + } else { + r0 = ret.Get(0).(database.User) + } + + if rf, ok := ret.Get(1).(func(context.Context, int32) error); ok { + r1 = rf(ctx, id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Store_GetUserByID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetUserByID' +type Store_GetUserByID_Call struct { + *mock.Call +} + +// GetUserByID is a helper method to define mock.On call +// - ctx context.Context +// - id int32 +func (_e *Store_Expecter) GetUserByID(ctx interface{}, id interface{}) *Store_GetUserByID_Call { + return &Store_GetUserByID_Call{Call: _e.mock.On("GetUserByID", ctx, id)} +} + +func (_c *Store_GetUserByID_Call) Run(run func(ctx context.Context, id int32)) *Store_GetUserByID_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(int32)) + }) + return _c +} + +func (_c *Store_GetUserByID_Call) Return(_a0 database.User, _a1 error) *Store_GetUserByID_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Store_GetUserByID_Call) RunAndReturn(run func(context.Context, int32) (database.User, error)) *Store_GetUserByID_Call { + _c.Call.Return(run) + return _c +} + +// GetUserByUID provides a mock function with given fields: ctx, id +func (_m *Store) GetUserByUID(ctx context.Context, id int32) (database.User, error) { + ret := _m.Called(ctx, id) + + if len(ret) == 0 { + panic("no return value specified for GetUserByUID") + } + + var r0 database.User + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int32) (database.User, error)); ok { + return rf(ctx, id) + } + if rf, ok := ret.Get(0).(func(context.Context, int32) database.User); ok { + r0 = rf(ctx, id) + } else { + r0 = ret.Get(0).(database.User) + } + + if rf, ok := ret.Get(1).(func(context.Context, int32) error); ok { + r1 = rf(ctx, id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Store_GetUserByUID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetUserByUID' +type Store_GetUserByUID_Call struct { + *mock.Call +} + +// GetUserByUID is a helper method to define mock.On call +// - ctx context.Context +// - id int32 +func (_e *Store_Expecter) GetUserByUID(ctx interface{}, id interface{}) *Store_GetUserByUID_Call { + return &Store_GetUserByUID_Call{Call: _e.mock.On("GetUserByUID", ctx, id)} +} + +func (_c *Store_GetUserByUID_Call) Run(run func(ctx context.Context, id int32)) *Store_GetUserByUID_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(int32)) + }) + return _c +} + +func (_c *Store_GetUserByUID_Call) Return(_a0 database.User, _a1 error) *Store_GetUserByUID_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Store_GetUserByUID_Call) RunAndReturn(run func(context.Context, int32) (database.User, error)) *Store_GetUserByUID_Call { + _c.Call.Return(run) + return _c +} + +// GetUserByUsername provides a mock function with given fields: ctx, username +func (_m *Store) GetUserByUsername(ctx context.Context, username string) (database.User, error) { + ret := _m.Called(ctx, username) + + if len(ret) == 0 { + panic("no return value specified for GetUserByUsername") + } + + var r0 database.User + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (database.User, error)); ok { + return rf(ctx, username) + } + if rf, ok := ret.Get(0).(func(context.Context, string) database.User); ok { + r0 = rf(ctx, username) + } else { + r0 = ret.Get(0).(database.User) + } + + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, username) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Store_GetUserByUsername_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetUserByUsername' +type Store_GetUserByUsername_Call struct { + *mock.Call +} + +// GetUserByUsername is a helper method to define mock.On call +// - ctx context.Context +// - username string +func (_e *Store_Expecter) GetUserByUsername(ctx interface{}, username interface{}) *Store_GetUserByUsername_Call { + return &Store_GetUserByUsername_Call{Call: _e.mock.On("GetUserByUsername", ctx, username)} +} + +func (_c *Store_GetUserByUsername_Call) Run(run func(ctx context.Context, username string)) *Store_GetUserByUsername_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string)) + }) + return _c +} + +func (_c *Store_GetUserByUsername_Call) Return(_a0 database.User, _a1 error) *Store_GetUserByUsername_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Store_GetUserByUsername_Call) RunAndReturn(run func(context.Context, string) (database.User, error)) *Store_GetUserByUsername_Call { + _c.Call.Return(run) + return _c +} + +// GetUsers provides a mock function with given fields: ctx +func (_m *Store) GetUsers(ctx context.Context) ([]database.User, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for GetUsers") + } + + var r0 []database.User + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) ([]database.User, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) []database.User); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]database.User) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Store_GetUsers_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetUsers' +type Store_GetUsers_Call struct { + *mock.Call +} + +// GetUsers is a helper method to define mock.On call +// - ctx context.Context +func (_e *Store_Expecter) GetUsers(ctx interface{}) *Store_GetUsers_Call { + return &Store_GetUsers_Call{Call: _e.mock.On("GetUsers", ctx)} +} + +func (_c *Store_GetUsers_Call) Run(run func(ctx context.Context)) *Store_GetUsers_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *Store_GetUsers_Call) Return(_a0 []database.User, _a1 error) *Store_GetUsers_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Store_GetUsers_Call) RunAndReturn(run func(context.Context) ([]database.User, error)) *Store_GetUsers_Call { + _c.Call.Return(run) + return _c +} + +// InTx provides a mock function with given fields: _a0, _a1, _a2 +func (_m *Store) InTx(_a0 context.Context, _a1 func(database.Store) error, _a2 pgx.TxOptions) error { + ret := _m.Called(_a0, _a1, _a2) + + if len(ret) == 0 { + panic("no return value specified for InTx") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, func(database.Store) error, pgx.TxOptions) error); ok { + r0 = rf(_a0, _a1, _a2) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Store_InTx_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'InTx' +type Store_InTx_Call struct { + *mock.Call +} + +// InTx is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 func(database.Store) error +// - _a2 pgx.TxOptions +func (_e *Store_Expecter) InTx(_a0 interface{}, _a1 interface{}, _a2 interface{}) *Store_InTx_Call { + return &Store_InTx_Call{Call: _e.mock.On("InTx", _a0, _a1, _a2)} +} + +func (_c *Store_InTx_Call) Run(run func(_a0 context.Context, _a1 func(database.Store) error, _a2 pgx.TxOptions)) *Store_InTx_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(func(database.Store) error), args[2].(pgx.TxOptions)) + }) + return _c +} + +func (_c *Store_InTx_Call) Return(_a0 error) *Store_InTx_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *Store_InTx_Call) RunAndReturn(run func(context.Context, func(database.Store) error, pgx.TxOptions) error) *Store_InTx_Call { + _c.Call.Return(run) + return _c +} + +// SetCallTranscript provides a mock function with given fields: ctx, iD, transcript +func (_m *Store) SetCallTranscript(ctx context.Context, iD uuid.UUID, transcript *string) error { + ret := _m.Called(ctx, iD, transcript) + + if len(ret) == 0 { + panic("no return value specified for SetCallTranscript") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, uuid.UUID, *string) error); ok { + r0 = rf(ctx, iD, transcript) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Store_SetCallTranscript_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetCallTranscript' +type Store_SetCallTranscript_Call struct { + *mock.Call +} + +// SetCallTranscript is a helper method to define mock.On call +// - ctx context.Context +// - iD uuid.UUID +// - transcript *string +func (_e *Store_Expecter) SetCallTranscript(ctx interface{}, iD interface{}, transcript interface{}) *Store_SetCallTranscript_Call { + return &Store_SetCallTranscript_Call{Call: _e.mock.On("SetCallTranscript", ctx, iD, transcript)} +} + +func (_c *Store_SetCallTranscript_Call) Run(run func(ctx context.Context, iD uuid.UUID, transcript *string)) *Store_SetCallTranscript_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(uuid.UUID), args[2].(*string)) + }) + return _c +} + +func (_c *Store_SetCallTranscript_Call) Return(_a0 error) *Store_SetCallTranscript_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *Store_SetCallTranscript_Call) RunAndReturn(run func(context.Context, uuid.UUID, *string) error) *Store_SetCallTranscript_Call { + _c.Call.Return(run) + return _c +} + +// SetTalkgroupTags provides a mock function with given fields: ctx, tags, systemID, tGID +func (_m *Store) SetTalkgroupTags(ctx context.Context, tags []string, systemID int32, tGID int32) error { + ret := _m.Called(ctx, tags, systemID, tGID) + + if len(ret) == 0 { + panic("no return value specified for SetTalkgroupTags") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, []string, int32, int32) error); ok { + r0 = rf(ctx, tags, systemID, tGID) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Store_SetTalkgroupTags_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetTalkgroupTags' +type Store_SetTalkgroupTags_Call struct { + *mock.Call +} + +// SetTalkgroupTags is a helper method to define mock.On call +// - ctx context.Context +// - tags []string +// - systemID int32 +// - tGID int32 +func (_e *Store_Expecter) SetTalkgroupTags(ctx interface{}, tags interface{}, systemID interface{}, tGID interface{}) *Store_SetTalkgroupTags_Call { + return &Store_SetTalkgroupTags_Call{Call: _e.mock.On("SetTalkgroupTags", ctx, tags, systemID, tGID)} +} + +func (_c *Store_SetTalkgroupTags_Call) Run(run func(ctx context.Context, tags []string, systemID int32, tGID int32)) *Store_SetTalkgroupTags_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].([]string), args[2].(int32), args[3].(int32)) + }) + return _c +} + +func (_c *Store_SetTalkgroupTags_Call) Return(_a0 error) *Store_SetTalkgroupTags_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *Store_SetTalkgroupTags_Call) RunAndReturn(run func(context.Context, []string, int32, int32) error) *Store_SetTalkgroupTags_Call { + _c.Call.Return(run) + return _c +} + +// UpdatePassword provides a mock function with given fields: ctx, username, password +func (_m *Store) UpdatePassword(ctx context.Context, username string, password string) error { + ret := _m.Called(ctx, username, password) + + if len(ret) == 0 { + panic("no return value specified for UpdatePassword") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { + r0 = rf(ctx, username, password) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Store_UpdatePassword_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdatePassword' +type Store_UpdatePassword_Call struct { + *mock.Call +} + +// UpdatePassword is a helper method to define mock.On call +// - ctx context.Context +// - username string +// - password string +func (_e *Store_Expecter) UpdatePassword(ctx interface{}, username interface{}, password interface{}) *Store_UpdatePassword_Call { + return &Store_UpdatePassword_Call{Call: _e.mock.On("UpdatePassword", ctx, username, password)} +} + +func (_c *Store_UpdatePassword_Call) Run(run func(ctx context.Context, username string, password string)) *Store_UpdatePassword_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string)) + }) + return _c +} + +func (_c *Store_UpdatePassword_Call) Return(_a0 error) *Store_UpdatePassword_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *Store_UpdatePassword_Call) RunAndReturn(run func(context.Context, string, string) error) *Store_UpdatePassword_Call { + _c.Call.Return(run) + return _c +} + +// UpdateTalkgroup provides a mock function with given fields: ctx, arg +func (_m *Store) UpdateTalkgroup(ctx context.Context, arg database.UpdateTalkgroupParams) (database.Talkgroup, error) { + ret := _m.Called(ctx, arg) + + if len(ret) == 0 { + panic("no return value specified for UpdateTalkgroup") + } + + var r0 database.Talkgroup + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, database.UpdateTalkgroupParams) (database.Talkgroup, error)); ok { + return rf(ctx, arg) + } + if rf, ok := ret.Get(0).(func(context.Context, database.UpdateTalkgroupParams) database.Talkgroup); ok { + r0 = rf(ctx, arg) + } else { + r0 = ret.Get(0).(database.Talkgroup) + } + + if rf, ok := ret.Get(1).(func(context.Context, database.UpdateTalkgroupParams) error); ok { + r1 = rf(ctx, arg) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Store_UpdateTalkgroup_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateTalkgroup' +type Store_UpdateTalkgroup_Call struct { + *mock.Call +} + +// UpdateTalkgroup is a helper method to define mock.On call +// - ctx context.Context +// - arg database.UpdateTalkgroupParams +func (_e *Store_Expecter) UpdateTalkgroup(ctx interface{}, arg interface{}) *Store_UpdateTalkgroup_Call { + return &Store_UpdateTalkgroup_Call{Call: _e.mock.On("UpdateTalkgroup", ctx, arg)} +} + +func (_c *Store_UpdateTalkgroup_Call) Run(run func(ctx context.Context, arg database.UpdateTalkgroupParams)) *Store_UpdateTalkgroup_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(database.UpdateTalkgroupParams)) + }) + return _c +} + +func (_c *Store_UpdateTalkgroup_Call) Return(_a0 database.Talkgroup, _a1 error) *Store_UpdateTalkgroup_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Store_UpdateTalkgroup_Call) RunAndReturn(run func(context.Context, database.UpdateTalkgroupParams) (database.Talkgroup, error)) *Store_UpdateTalkgroup_Call { + _c.Call.Return(run) + return _c +} + +// NewStore creates a new instance of Store. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewStore(t interface { + mock.TestingT + Cleanup(func()) +}) *Store { + mock := &Store{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/database/models.go b/pkg/database/models.go index bd113fb..987df9c 100644 --- a/pkg/database/models.go +++ b/pkg/database/models.go @@ -14,7 +14,7 @@ import ( ) type Alert struct { - ID uuid.UUID `json:"id"` + ID int `json:"id"` Time pgtype.Timestamptz `json:"time"` TGID int `json:"tgid"` SystemID int `json:"system_id"` @@ -48,9 +48,9 @@ type Call struct { Frequency int `json:"frequency"` Frequencies []int `json:"frequencies"` Patches []int `json:"patches"` - TgLabel *string `json:"tg_label"` - TgAlphaTag *string `json:"tg_alpha_tag"` - TgGroup *string `json:"tg_group"` + TGLabel *string `json:"tg_label"` + TGAlphaTag *string `json:"tg_alpha_tag"` + TGGroup *string `json:"tg_group"` Source int `json:"source"` Transcript *string `json:"transcript"` } @@ -83,27 +83,29 @@ type System struct { } type Talkgroup struct { - ID uuid.UUID `json:"id"` + ID int `json:"id"` SystemID int32 `json:"system_id"` TGID int32 `json:"tgid"` Name *string `json:"name"` AlphaTag *string `json:"alpha_tag"` - TgGroup *string `json:"tg_group"` + TGGroup *string `json:"tg_group"` Frequency *int32 `json:"frequency"` Metadata jsontypes.Metadata `json:"metadata"` Tags []string `json:"tags"` Alert bool `json:"alert"` AlertConfig rules.AlertRules `json:"alert_config"` Weight float32 `json:"weight"` + Learned bool `json:"learned"` } type TalkgroupsLearned struct { - ID uuid.UUID `json:"id"` - SystemID int `json:"system_id"` - TGID int `json:"tgid"` - Name string `json:"name"` - AlphaTag *string `json:"alpha_tag"` - Ignored *bool `json:"ignored"` + ID int `json:"id"` + SystemID int `json:"system_id"` + TGID int `json:"tgid"` + Name string `json:"name"` + AlphaTag *string `json:"alpha_tag"` + TGGroup *string `json:"tg_group"` + Ignored *bool `json:"ignored"` } type User struct { diff --git a/pkg/database/querier.go b/pkg/database/querier.go index cd63b24..d1358a4 100644 --- a/pkg/database/querier.go +++ b/pkg/database/querier.go @@ -14,6 +14,8 @@ import ( type Querier interface { AddAlert(ctx context.Context, arg AddAlertParams) error AddCall(ctx context.Context, arg AddCallParams) error + AddLearnedTalkgroup(ctx context.Context, arg AddLearnedTalkgroupParams) (int, error) + AddTalkgroupWithLearnedFlag(ctx context.Context, systemID int32, tGID int32) error CreateAPIKey(ctx context.Context, owner int, expires pgtype.Timestamp, disabled *bool) (ApiKey, error) CreateUser(ctx context.Context, arg CreateUserParams) (User, error) DeleteAPIKey(ctx context.Context, apiKey string) error @@ -21,9 +23,9 @@ type Querier interface { GetAPIKey(ctx context.Context, apiKey string) (ApiKey, error) GetDatabaseSize(ctx context.Context) (string, error) GetSystemName(ctx context.Context, systemID int) (string, error) - GetTalkgroup(ctx context.Context, systemID int32, tgID int32) (GetTalkgroupRow, error) + GetTalkgroup(ctx context.Context, systemID int32, tGID int32) (GetTalkgroupRow, error) GetTalkgroupIDsByTags(ctx context.Context, anyTags []string, allTags []string, notTags []string) ([]GetTalkgroupIDsByTagsRow, error) - GetTalkgroupTags(ctx context.Context, systemID int32, tgID int32) ([]string, error) + GetTalkgroupTags(ctx context.Context, systemID int32, tGID int32) ([]string, error) GetTalkgroupWithLearned(ctx context.Context, systemID int32, tGID int32) (GetTalkgroupWithLearnedRow, error) GetTalkgroupsWithAllTags(ctx context.Context, tags []string) ([]GetTalkgroupsWithAllTagsRow, error) GetTalkgroupsWithAnyTags(ctx context.Context, tags []string) ([]GetTalkgroupsWithAnyTagsRow, error) @@ -34,7 +36,7 @@ type Querier interface { GetUserByUsername(ctx context.Context, username string) (User, error) GetUsers(ctx context.Context) ([]User, error) SetCallTranscript(ctx context.Context, iD uuid.UUID, transcript *string) error - SetTalkgroupTags(ctx context.Context, tags []string, systemID int32, tgID int32) error + SetTalkgroupTags(ctx context.Context, tags []string, systemID int32, tGID int32) error UpdatePassword(ctx context.Context, username string, password string) error UpdateTalkgroup(ctx context.Context, arg UpdateTalkgroupParams) (Talkgroup, error) } diff --git a/pkg/database/talkgroups.go b/pkg/database/talkgroups.go index 276b0d8..a461dcd 100644 --- a/pkg/database/talkgroups.go +++ b/pkg/database/talkgroups.go @@ -2,6 +2,9 @@ package database import ( "context" + "errors" + + "github.com/jackc/pgx/v5/pgconn" ) type talkgroupQuerier interface { @@ -12,6 +15,17 @@ type talkgroupQuerier interface { type TGTuples [2][]uint32 +const TGConstraintName = "calls_system_talkgroup_fkey" + +func IsTGConstraintViolation(e error) bool { + var err *pgconn.PgError + if errors.As(e, &err) && err.Code == "23503" && err.ConstraintName == TGConstraintName { + return true + } + + return false +} + func MakeTGTuples(cap int) TGTuples { return [2][]uint32{ make([]uint32, 0, cap), @@ -27,18 +41,17 @@ func (t *TGTuples) Append(sys, tg uint32) { // Below queries are here because sqlc refuses to parse unnest(x, y) const getTalkgroupsWithLearnedBySysTGID = `SELECT -tg.id, 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, sys.id, sys.name, -FALSE learned +tg.id, 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, sys.id, sys.name, tg.learned FROM talkgroups tg JOIN systems sys ON tg.system_id = sys.id JOIN UNNEST($1::INT4[], $2::INT4[]) AS tgt(sys, tg) ON (tg.system_id = tgt.sys AND tg.tgid = tgt.tg) +WHERE tg.learned IS NOT TRUE UNION SELECT tgl.id, tgl.system_id::INT4, tgl.tgid::INT4, tgl.name, -tgl.alpha_tag, tgl.alpha_tag, NULL::INTEGER, NULL::JSONB, -CASE WHEN tgl.alpha_tag IS NULL THEN NULL ELSE ARRAY[tgl.alpha_tag] END, -TRUE, NULL::JSONB, 1.0, sys.id, sys.name, -TRUE learned +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, +TRUE, NULL::JSONB, 1.0, sys.id, sys.name, TRUE learned FROM talkgroups_learned tgl JOIN systems sys ON tgl.system_id = sys.id JOIN UNNEST($1::INT4[], $2::INT4[]) AS tgt(sys, tg) ON (tgl.system_id = tgt.sys AND tgl.tgid = tgt.tg);` @@ -46,7 +59,6 @@ JOIN UNNEST($1::INT4[], $2::INT4[]) AS tgt(sys, tg) ON (tgl.system_id = tgt.sys type GetTalkgroupsRow struct { Talkgroup Talkgroup `json:"talkgroup"` System System `json:"system"` - Learned bool `json:"learned"` } func (q *Queries) GetTalkgroupsWithLearnedBySysTGID(ctx context.Context, ids TGTuples) ([]GetTalkgroupsRow, error) { @@ -64,7 +76,7 @@ func (q *Queries) GetTalkgroupsWithLearnedBySysTGID(ctx context.Context, ids TGT &i.Talkgroup.TGID, &i.Talkgroup.Name, &i.Talkgroup.AlphaTag, - &i.Talkgroup.TgGroup, + &i.Talkgroup.TGGroup, &i.Talkgroup.Frequency, &i.Talkgroup.Metadata, &i.Talkgroup.Tags, @@ -73,7 +85,7 @@ func (q *Queries) GetTalkgroupsWithLearnedBySysTGID(ctx context.Context, ids TGT &i.Talkgroup.Weight, &i.System.ID, &i.System.Name, - &i.Learned, + &i.Talkgroup.Learned, ); err != nil { return nil, err } @@ -87,7 +99,8 @@ func (q *Queries) GetTalkgroupsWithLearnedBySysTGID(ctx context.Context, ids TGT const getTalkgroupsBySysTGID = `SELECT tg.id, 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, sys.id, sys.name FROM talkgroups tg JOIN systems sys ON tg.system_id = sys.id -JOIN UNNEST($1::INT4[], $2::INT4[]) AS tgt(sys, tg) ON (tg.system_id = tgt.sys AND tg.tgid = tgt.tg);` +JOIN UNNEST($1::INT4[], $2::INT4[]) AS tgt(sys, tg) ON (tg.system_id = tgt.sys AND tg.tgid = tgt.tg) +WHERE tg.learned IS NOT TRUE;` func (q *Queries) GetTalkgroupsBySysTGID(ctx context.Context, ids TGTuples) ([]GetTalkgroupsRow, error) { rows, err := q.db.Query(ctx, getTalkgroupsBySysTGID, ids[0], ids[1]) @@ -104,7 +117,7 @@ func (q *Queries) GetTalkgroupsBySysTGID(ctx context.Context, ids TGTuples) ([]G &i.Talkgroup.TGID, &i.Talkgroup.Name, &i.Talkgroup.AlphaTag, - &i.Talkgroup.TgGroup, + &i.Talkgroup.TGGroup, &i.Talkgroup.Frequency, &i.Talkgroup.Metadata, &i.Talkgroup.Tags, diff --git a/pkg/database/talkgroups.sql.go b/pkg/database/talkgroups.sql.go index a369ff2..9291314 100644 --- a/pkg/database/talkgroups.sql.go +++ b/pkg/database/talkgroups.sql.go @@ -10,9 +10,62 @@ import ( "dynatron.me/x/stillbox/internal/jsontypes" "dynatron.me/x/stillbox/pkg/alerting/rules" - "github.com/jackc/pgx/v5/pgtype" ) +const addLearnedTalkgroup = `-- name: AddLearnedTalkgroup :one +INSERT INTO talkgroups_learned( + system_id, + tgid, + name, + alpha_tag, + tg_group +) VALUES ( + $1, + $2, + $3, + $4, + $5 +) RETURNING id +` + +type AddLearnedTalkgroupParams struct { + SystemID int `json:"system_id"` + TGID int `json:"tgid"` + Name *string `json:"name"` + AlphaTag *string `json:"alpha_tag"` + TGGroup *string `json:"tg_group"` +} + +func (q *Queries) AddLearnedTalkgroup(ctx context.Context, arg AddLearnedTalkgroupParams) (int, error) { + row := q.db.QueryRow(ctx, addLearnedTalkgroup, + arg.SystemID, + arg.TGID, + arg.Name, + arg.AlphaTag, + arg.TGGroup, + ) + var id int + err := row.Scan(&id) + return id, err +} + +const addTalkgroupWithLearnedFlag = `-- name: AddTalkgroupWithLearnedFlag :exec +INSERT INTO talkgroups ( + system_id, + tgid, + learned +) VALUES( + $1, + $2, + 't' +) +` + +func (q *Queries) AddTalkgroupWithLearnedFlag(ctx context.Context, systemID int32, tGID int32) error { + _, err := q.db.Exec(ctx, addTalkgroupWithLearnedFlag, systemID, tGID) + return err +} + const getSystemName = `-- name: GetSystemName :one SELECT name FROM systems WHERE id = $1 ` @@ -25,7 +78,7 @@ func (q *Queries) GetSystemName(ctx context.Context, systemID int) (string, erro } const getTalkgroup = `-- name: GetTalkgroup :one -SELECT talkgroups.id, talkgroups.system_id, talkgroups.tgid, talkgroups.name, talkgroups.alpha_tag, talkgroups.tg_group, talkgroups.frequency, talkgroups.metadata, talkgroups.tags, talkgroups.alert, talkgroups.alert_config, talkgroups.weight FROM talkgroups +SELECT talkgroups.id, talkgroups.system_id, talkgroups.tgid, talkgroups.name, talkgroups.alpha_tag, talkgroups.tg_group, talkgroups.frequency, talkgroups.metadata, talkgroups.tags, talkgroups.alert, talkgroups.alert_config, talkgroups.weight, talkgroups.learned FROM talkgroups WHERE (system_id, tgid) = ($1, $2) ` @@ -33,8 +86,8 @@ type GetTalkgroupRow struct { Talkgroup Talkgroup `json:"talkgroup"` } -func (q *Queries) GetTalkgroup(ctx context.Context, systemID int32, tgID int32) (GetTalkgroupRow, error) { - row := q.db.QueryRow(ctx, getTalkgroup, systemID, tgID) +func (q *Queries) GetTalkgroup(ctx context.Context, systemID int32, tGID int32) (GetTalkgroupRow, error) { + row := q.db.QueryRow(ctx, getTalkgroup, systemID, tGID) var i GetTalkgroupRow err := row.Scan( &i.Talkgroup.ID, @@ -42,13 +95,14 @@ func (q *Queries) GetTalkgroup(ctx context.Context, systemID int32, tgID int32) &i.Talkgroup.TGID, &i.Talkgroup.Name, &i.Talkgroup.AlphaTag, - &i.Talkgroup.TgGroup, + &i.Talkgroup.TGGroup, &i.Talkgroup.Frequency, &i.Talkgroup.Metadata, &i.Talkgroup.Tags, &i.Talkgroup.Alert, &i.Talkgroup.AlertConfig, &i.Talkgroup.Weight, + &i.Talkgroup.Learned, ) return i, err } @@ -90,8 +144,8 @@ SELECT tags FROM talkgroups WHERE system_id = $1 AND tgid = $2 ` -func (q *Queries) GetTalkgroupTags(ctx context.Context, systemID int32, tgID int32) ([]string, error) { - row := q.db.QueryRow(ctx, getTalkgroupTags, systemID, tgID) +func (q *Queries) GetTalkgroupTags(ctx context.Context, systemID int32, tGID int32) ([]string, error) { + row := q.db.QueryRow(ctx, getTalkgroupTags, systemID, tGID) var tags []string err := row.Scan(&tags) return tags, err @@ -99,18 +153,16 @@ func (q *Queries) GetTalkgroupTags(ctx context.Context, systemID int32, tgID int const getTalkgroupWithLearned = `-- name: GetTalkgroupWithLearned :one SELECT -tg.id, 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, sys.id, sys.name, -FALSE learned +tg.id, 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, sys.id, sys.name FROM talkgroups tg JOIN systems sys ON tg.system_id = sys.id -WHERE (tg.system_id, tg.tgid) = ($1, $2) +WHERE (tg.system_id, tg.tgid) = ($1, $2) AND tg.learned IS NOT TRUE UNION SELECT tgl.id, tgl.system_id::INT4, tgl.tgid::INT4, tgl.name, -tgl.alpha_tag, tgl.alpha_tag, NULL::INTEGER, NULL::JSONB, -CASE WHEN tgl.alpha_tag IS NULL THEN NULL ELSE ARRAY[tgl.alpha_tag] END, -TRUE, NULL::JSONB, 1.0, sys.id, sys.name, -TRUE learned +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, +TRUE, 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 = $1 AND tgl.tgid = $2 AND ignored IS NOT TRUE @@ -119,7 +171,6 @@ WHERE tgl.system_id = $1 AND tgl.tgid = $2 AND ignored IS NOT TRUE type GetTalkgroupWithLearnedRow struct { Talkgroup Talkgroup `json:"talkgroup"` System System `json:"system"` - Learned bool `json:"learned"` } func (q *Queries) GetTalkgroupWithLearned(ctx context.Context, systemID int32, tGID int32) (GetTalkgroupWithLearnedRow, error) { @@ -131,22 +182,22 @@ func (q *Queries) GetTalkgroupWithLearned(ctx context.Context, systemID int32, t &i.Talkgroup.TGID, &i.Talkgroup.Name, &i.Talkgroup.AlphaTag, - &i.Talkgroup.TgGroup, + &i.Talkgroup.TGGroup, &i.Talkgroup.Frequency, &i.Talkgroup.Metadata, &i.Talkgroup.Tags, &i.Talkgroup.Alert, &i.Talkgroup.AlertConfig, &i.Talkgroup.Weight, + &i.Talkgroup.Learned, &i.System.ID, &i.System.Name, - &i.Learned, ) return i, err } const getTalkgroupsWithAllTags = `-- name: GetTalkgroupsWithAllTags :many -SELECT talkgroups.id, talkgroups.system_id, talkgroups.tgid, talkgroups.name, talkgroups.alpha_tag, talkgroups.tg_group, talkgroups.frequency, talkgroups.metadata, talkgroups.tags, talkgroups.alert, talkgroups.alert_config, talkgroups.weight FROM talkgroups +SELECT talkgroups.id, talkgroups.system_id, talkgroups.tgid, talkgroups.name, talkgroups.alpha_tag, talkgroups.tg_group, talkgroups.frequency, talkgroups.metadata, talkgroups.tags, talkgroups.alert, talkgroups.alert_config, talkgroups.weight, talkgroups.learned FROM talkgroups WHERE tags && ARRAY[$1] ` @@ -169,13 +220,14 @@ func (q *Queries) GetTalkgroupsWithAllTags(ctx context.Context, tags []string) ( &i.Talkgroup.TGID, &i.Talkgroup.Name, &i.Talkgroup.AlphaTag, - &i.Talkgroup.TgGroup, + &i.Talkgroup.TGGroup, &i.Talkgroup.Frequency, &i.Talkgroup.Metadata, &i.Talkgroup.Tags, &i.Talkgroup.Alert, &i.Talkgroup.AlertConfig, &i.Talkgroup.Weight, + &i.Talkgroup.Learned, ); err != nil { return nil, err } @@ -188,7 +240,7 @@ func (q *Queries) GetTalkgroupsWithAllTags(ctx context.Context, tags []string) ( } const getTalkgroupsWithAnyTags = `-- name: GetTalkgroupsWithAnyTags :many -SELECT talkgroups.id, talkgroups.system_id, talkgroups.tgid, talkgroups.name, talkgroups.alpha_tag, talkgroups.tg_group, talkgroups.frequency, talkgroups.metadata, talkgroups.tags, talkgroups.alert, talkgroups.alert_config, talkgroups.weight FROM talkgroups +SELECT talkgroups.id, talkgroups.system_id, talkgroups.tgid, talkgroups.name, talkgroups.alpha_tag, talkgroups.tg_group, talkgroups.frequency, talkgroups.metadata, talkgroups.tags, talkgroups.alert, talkgroups.alert_config, talkgroups.weight, talkgroups.learned FROM talkgroups WHERE tags @> ARRAY[$1] ` @@ -211,13 +263,14 @@ func (q *Queries) GetTalkgroupsWithAnyTags(ctx context.Context, tags []string) ( &i.Talkgroup.TGID, &i.Talkgroup.Name, &i.Talkgroup.AlphaTag, - &i.Talkgroup.TgGroup, + &i.Talkgroup.TGGroup, &i.Talkgroup.Frequency, &i.Talkgroup.Metadata, &i.Talkgroup.Tags, &i.Talkgroup.Alert, &i.Talkgroup.AlertConfig, &i.Talkgroup.Weight, + &i.Talkgroup.Learned, ); err != nil { return nil, err } @@ -231,17 +284,16 @@ func (q *Queries) GetTalkgroupsWithAnyTags(ctx context.Context, tags []string) ( const getTalkgroupsWithLearned = `-- name: GetTalkgroupsWithLearned :many SELECT -tg.id, 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, sys.id, sys.name, -FALSE learned +tg.id, 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, sys.id, sys.name FROM talkgroups tg 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.alpha_tag, NULL::INTEGER, NULL::JSONB, -CASE WHEN tgl.alpha_tag IS NULL THEN NULL ELSE ARRAY[tgl.alpha_tag] END, -TRUE, NULL::JSONB, 1.0, sys.id, sys.name, -TRUE learned +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, +TRUE, 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 @@ -250,7 +302,6 @@ WHERE ignored IS NOT TRUE type GetTalkgroupsWithLearnedRow struct { Talkgroup Talkgroup `json:"talkgroup"` System System `json:"system"` - Learned bool `json:"learned"` } func (q *Queries) GetTalkgroupsWithLearned(ctx context.Context) ([]GetTalkgroupsWithLearnedRow, error) { @@ -268,16 +319,16 @@ func (q *Queries) GetTalkgroupsWithLearned(ctx context.Context) ([]GetTalkgroups &i.Talkgroup.TGID, &i.Talkgroup.Name, &i.Talkgroup.AlphaTag, - &i.Talkgroup.TgGroup, + &i.Talkgroup.TGGroup, &i.Talkgroup.Frequency, &i.Talkgroup.Metadata, &i.Talkgroup.Tags, &i.Talkgroup.Alert, &i.Talkgroup.AlertConfig, &i.Talkgroup.Weight, + &i.Talkgroup.Learned, &i.System.ID, &i.System.Name, - &i.Learned, ); err != nil { return nil, err } @@ -291,18 +342,16 @@ func (q *Queries) GetTalkgroupsWithLearned(ctx context.Context) ([]GetTalkgroups const getTalkgroupsWithLearnedBySystem = `-- name: GetTalkgroupsWithLearnedBySystem :many SELECT -tg.id, 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, sys.id, sys.name, -FALSE learned +tg.id, 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, sys.id, sys.name FROM talkgroups tg JOIN systems sys ON tg.system_id = sys.id -WHERE tg.system_id = $1 +WHERE tg.system_id = $1 AND tg.learned IS NOT TRUE UNION SELECT tgl.id, tgl.system_id::INT4, tgl.tgid::INT4, tgl.name, -tgl.alpha_tag, tgl.alpha_tag, NULL::INTEGER, NULL::JSONB, -CASE WHEN tgl.alpha_tag IS NULL THEN NULL ELSE ARRAY[tgl.alpha_tag] END, -TRUE, NULL::JSONB, 1.0, sys.id, sys.name, -TRUE learned +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, +TRUE, 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 = $1 AND ignored IS NOT TRUE @@ -311,7 +360,6 @@ WHERE tgl.system_id = $1 AND ignored IS NOT TRUE type GetTalkgroupsWithLearnedBySystemRow struct { Talkgroup Talkgroup `json:"talkgroup"` System System `json:"system"` - Learned bool `json:"learned"` } func (q *Queries) GetTalkgroupsWithLearnedBySystem(ctx context.Context, system int32) ([]GetTalkgroupsWithLearnedBySystemRow, error) { @@ -329,16 +377,16 @@ func (q *Queries) GetTalkgroupsWithLearnedBySystem(ctx context.Context, system i &i.Talkgroup.TGID, &i.Talkgroup.Name, &i.Talkgroup.AlphaTag, - &i.Talkgroup.TgGroup, + &i.Talkgroup.TGGroup, &i.Talkgroup.Frequency, &i.Talkgroup.Metadata, &i.Talkgroup.Tags, &i.Talkgroup.Alert, &i.Talkgroup.AlertConfig, &i.Talkgroup.Weight, + &i.Talkgroup.Learned, &i.System.ID, &i.System.Name, - &i.Learned, ); err != nil { return nil, err } @@ -355,8 +403,8 @@ UPDATE talkgroups SET tags = $1 WHERE system_id = $2 AND tgid = $3 ` -func (q *Queries) SetTalkgroupTags(ctx context.Context, tags []string, systemID int32, tgID int32) error { - _, err := q.db.Exec(ctx, setTalkgroupTags, tags, systemID, tgID) +func (q *Queries) SetTalkgroupTags(ctx context.Context, tags []string, systemID int32, tGID int32) error { + _, err := q.db.Exec(ctx, setTalkgroupTags, tags, systemID, tGID) return err } @@ -373,20 +421,20 @@ SET alert_config = COALESCE($8, alert_config), weight = COALESCE($9, weight) WHERE id = $10 OR (system_id = $11 AND tgid = $12) -RETURNING id, system_id, tgid, name, alpha_tag, tg_group, frequency, metadata, tags, alert, alert_config, weight +RETURNING id, system_id, tgid, name, alpha_tag, tg_group, frequency, metadata, tags, alert, alert_config, weight, learned ` type UpdateTalkgroupParams struct { Name *string `json:"name"` AlphaTag *string `json:"alpha_tag"` - TgGroup *string `json:"tg_group"` + TGGroup *string `json:"tg_group"` Frequency *int32 `json:"frequency"` Metadata jsontypes.Metadata `json:"metadata"` Tags []string `json:"tags"` Alert *bool `json:"alert"` AlertConfig rules.AlertRules `json:"alert_config"` Weight *float32 `json:"weight"` - ID pgtype.UUID `json:"id"` + ID *int32 `json:"id"` SystemID *int32 `json:"system_id"` TGID *int32 `json:"tgid"` } @@ -395,7 +443,7 @@ func (q *Queries) UpdateTalkgroup(ctx context.Context, arg UpdateTalkgroupParams row := q.db.QueryRow(ctx, updateTalkgroup, arg.Name, arg.AlphaTag, - arg.TgGroup, + arg.TGGroup, arg.Frequency, arg.Metadata, arg.Tags, @@ -413,13 +461,14 @@ func (q *Queries) UpdateTalkgroup(ctx context.Context, arg UpdateTalkgroupParams &i.TGID, &i.Name, &i.AlphaTag, - &i.TgGroup, + &i.TGGroup, &i.Frequency, &i.Metadata, &i.Tags, &i.Alert, &i.AlertConfig, &i.Weight, + &i.Learned, ) return i, err } diff --git a/pkg/nexus/commands.go b/pkg/nexus/commands.go index 502859a..4c4bcaa 100644 --- a/pkg/nexus/commands.go +++ b/pkg/nexus/commands.go @@ -78,7 +78,7 @@ func (c *client) Talkgroup(ctx context.Context, tg *pb.Talkgroup) error { resp := &pb.TalkgroupInfo{ Tg: tg, Name: tgi.Talkgroup.Name, - Group: tgi.Talkgroup.TgGroup, + Group: tgi.Talkgroup.TGGroup, Frequency: tgi.Talkgroup.Frequency, Metadata: md, Tags: tgi.Talkgroup.Tags, diff --git a/pkg/server/server.go b/pkg/server/server.go index 4357d12..bc55ae3 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -27,7 +27,7 @@ const shutdownTimeout = 5 * time.Second type Server struct { auth *auth.Auth conf *config.Config - db database.DB + db database.Store r *chi.Mux sources sources.Sources sinks sinks.Sinks @@ -112,7 +112,7 @@ func New(ctx context.Context, cfg *config.Config) (*Server, error) { } func (s *Server) Go(ctx context.Context) error { - defer s.db.DB().Close() + defer database.Close(s.db) s.installHupHandler() diff --git a/pkg/sinks/database.go b/pkg/sinks/database.go index fa557cc..9ced102 100644 --- a/pkg/sinks/database.go +++ b/pkg/sinks/database.go @@ -8,16 +8,17 @@ import ( "dynatron.me/x/stillbox/pkg/calls" "dynatron.me/x/stillbox/pkg/database" + "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgtype" "github.com/rs/zerolog/log" ) type DatabaseSink struct { - db database.DB + db database.Store } -func NewDatabaseSink(db database.DB) *DatabaseSink { - return &DatabaseSink{db: db} +func NewDatabaseSink(store database.Store) *DatabaseSink { + return &DatabaseSink{store} } func (s *DatabaseSink) Call(ctx context.Context, call *calls.Call) error { @@ -26,14 +27,37 @@ func (s *DatabaseSink) Call(ctx context.Context, call *calls.Call) error { return nil } - err := s.db.AddCall(ctx, s.toAddCallParams(call)) - if err != nil { - return fmt.Errorf("add call: %w", err) + params := s.toAddCallParams(call) + + err := s.db.InTx(ctx, func(tx database.Store) error { + err := tx.AddCall(ctx, params) + if err != nil { + + return fmt.Errorf("add call: %w", err) + } + + log.Debug().Str("id", call.ID.String()).Int("system", call.System).Int("tgid", call.Talkgroup).Msg("stored") + + return nil + }, pgx.TxOptions{}) + + if err != nil && database.IsTGConstraintViolation(err) { + return s.db.InTx(ctx, func(tx database.Store) error { + _, err := call.LearnTG(ctx, tx) + if err != nil { + return fmt.Errorf("add call: learn tg: %w", err) + } + + err = tx.AddCall(ctx, params) + if err != nil { + return fmt.Errorf("add call: retry: %w", err) + } + + return nil + }, pgx.TxOptions{}) } - log.Debug().Str("id", call.ID.String()).Int("system", call.System).Int("tgid", call.Talkgroup).Msg("stored") - - return nil + return err } func (s *DatabaseSink) SinkType() string { @@ -54,9 +78,9 @@ func (s *DatabaseSink) toAddCallParams(call *calls.Call) database.AddCallParams Frequency: call.Frequency, Frequencies: call.Frequencies, Patches: call.Patches, - TgLabel: call.TalkgroupLabel, - TgAlphaTag: call.TGAlphaTag, - TgGroup: call.TalkgroupGroup, + TGLabel: call.TalkgroupLabel, + TGAlphaTag: call.TGAlphaTag, + TGGroup: call.TalkgroupGroup, Source: call.Source, } } diff --git a/pkg/talkgroups/cache.go b/pkg/talkgroups/cache.go index 9d0184b..0d58a16 100644 --- a/pkg/talkgroups/cache.go +++ b/pkg/talkgroups/cache.go @@ -252,7 +252,7 @@ func (t *cache) TG(ctx context.Context, tg ID) (*Talkgroup, error) { case pgx.ErrNoRows: return nil, ErrNotFound default: - log.Error().Err(err).Msg("TG() cache add db get") + log.Error().Err(err).Uint32("sys", tg.System).Uint32("tg", tg.Talkgroup).Msg("TG() cache add db get") return nil, errors.Join(ErrNotFound, err) } diff --git a/pkg/talkgroups/importer/import.go b/pkg/talkgroups/importer/import.go index 82730fe..b616a3b 100644 --- a/pkg/talkgroups/importer/import.go +++ b/pkg/talkgroups/importer/import.go @@ -10,8 +10,6 @@ import ( "strconv" "strings" - "github.com/google/uuid" - "dynatron.me/x/stillbox/internal/jsontypes" "dynatron.me/x/stillbox/pkg/database" "dynatron.me/x/stillbox/pkg/talkgroups" @@ -112,12 +110,11 @@ func (rr *radioReferenceImporter) importTalkgroups(ctx context.Context, sys int, gn := groupName // must take a copy tgs = append(tgs, talkgroups.Talkgroup{ Talkgroup: database.Talkgroup{ - ID: uuid.New(), TGID: int32(tgt.Talkgroup), SystemID: int32(tgt.System), Name: &fields[4], AlphaTag: &fields[3], - TgGroup: &gn, + TGGroup: &gn, Metadata: metadata, Tags: tags, Weight: 1.0, diff --git a/sql/postgres/migrations/001_initial.up.sql b/sql/postgres/migrations/001_initial.up.sql index cd37bc9..40d49ee 100644 --- a/sql/postgres/migrations/001_initial.up.sql +++ b/sql/postgres/migrations/001_initial.up.sql @@ -44,17 +44,18 @@ CREATE INDEX talkgroups_system_tgid_idx ON talkgroups (system_id, tgid); CREATE INDEX IF NOT EXISTS talkgroup_id_tags ON talkgroups USING GIN (tags); CREATE TABLE IF NOT EXISTS talkgroups_learned( - id UUID PRIMARY KEY, + id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, system_id INTEGER REFERENCES systems(id) NOT NULL, tgid INTEGER NOT NULL, name TEXT NOT NULL, alpha_tag TEXT, + tg_group TEXT, ignored BOOLEAN, UNIQUE (system_id, tgid, name) ); CREATE TABLE IF NOT EXISTS alerts( - id UUID PRIMARY KEY, + id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, time TIMESTAMPTZ NOT NULL, tgid INTEGER NOT NULL, system_id INTEGER REFERENCES systems(id) NOT NULL, @@ -65,22 +66,6 @@ CREATE TABLE IF NOT EXISTS alerts( metadata JSONB ); -CREATE OR REPLACE FUNCTION learn_talkgroup() -RETURNS TRIGGER AS $$ -BEGIN - IF NOT EXISTS ( - SELECT tg.system_id, tg.tgid, tg.name, tg.alpha_tag FROM talkgroups tg WHERE tg.system_id = NEW.system AND tg.tgid = NEW.talkgroup - UNION - SELECT tgl.system_id, tgl.tgid, tgl.name, tgl.alpha_tag FROM talkgroups_learned tgl WHERE tgl.system_id = NEW.system AND tgl.tgid = NEW.talkgroup - ) THEN - INSERT INTO talkgroups_learned(system_id, tgid, name, alpha_tag) VALUES( - NEW.system, NEW.talkgroup, NEW.tg_label, NEW.tg_alpha_tag - ) ON CONFLICT DO NOTHING; - END IF; - RETURN NEW; -END -$$ LANGUAGE plpgsql; - CREATE TABLE IF NOT EXISTS calls( id UUID PRIMARY KEY, submitter INTEGER REFERENCES api_keys(id) ON DELETE SET NULL, @@ -102,9 +87,6 @@ CREATE TABLE IF NOT EXISTS calls( transcript TEXT ); -CREATE OR REPLACE TRIGGER learn_tg AFTER INSERT ON calls -FOR EACH ROW EXECUTE FUNCTION learn_talkgroup(); - CREATE INDEX IF NOT EXISTS calls_transcript_idx ON calls USING GIN (to_tsvector('english', transcript)); CREATE INDEX IF NOT EXISTS calls_call_date_tg_idx ON calls(system, talkgroup, call_date); diff --git a/sql/postgres/migrations/002_tglearned.down.sql b/sql/postgres/migrations/002_tglearned.down.sql new file mode 100644 index 0000000..7355bf1 --- /dev/null +++ b/sql/postgres/migrations/002_tglearned.down.sql @@ -0,0 +1,25 @@ +ALTER TABLE calls DROP CONSTRAINT IF EXISTS calls_system_talkgroup_fkey; + +ALTER TABLE talkgroups DROP COLUMN IF EXISTS learned; + +ALTER TABLE talkgroups ALTER COLUMN id DROP IDENTITY IF EXISTS; +ALTER TABLE talkgroups ALTER COLUMN id SET DATA TYPE UUID USING (gen_random_uuid()); +DROP SEQUENCE IF EXISTS talkgroups_id_seq; + +CREATE OR REPLACE FUNCTION learn_talkgroup() +RETURNS TRIGGER AS $$ +BEGIN + IF NOT EXISTS ( + SELECT tg.system_id, tg.tgid, tg.name, tg.alpha_tag FROM talkgroups tg WHERE tg.system_id = NEW.system AND tg.tgid = NEW.talkgroup + UNION + SELECT tgl.system_id, tgl.tgid, tgl.name, tgl.alpha_tag FROM talkgroups_learned tgl WHERE tgl.system_id = NEW.system AND tgl.tgid = NEW.talkgroup + ) THEN + INSERT INTO talkgroups_learned(system_id, tgid, name, alpha_tag) VALUES( + NEW.system, NEW.talkgroup, NEW.tg_label, NEW.tg_alpha_tag + ) ON CONFLICT DO NOTHING; + END IF; + RETURN NEW; +END +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE TRIGGER learn_tg AFTER INSERT ON calls FOR EACH ROW EXECUTE FUNCTION learn_talkgroup(); diff --git a/sql/postgres/migrations/002_tglearned.up.sql b/sql/postgres/migrations/002_tglearned.up.sql new file mode 100644 index 0000000..21f5cd2 --- /dev/null +++ b/sql/postgres/migrations/002_tglearned.up.sql @@ -0,0 +1,24 @@ +DROP TRIGGER IF EXISTS learn_tg ON calls; +DROP FUNCTION IF EXISTS learn_talkgroup(); + +CREATE SEQUENCE IF NOT EXISTS talkgroups_id_seq START WITH 1; +ALTER TABLE talkgroups ALTER COLUMN id SET DATA TYPE INTEGER USING (nextval('talkgroups_id_seq')); +ALTER TABLE talkgroups ALTER COLUMN id ADD GENERATED ALWAYS AS IDENTITY; +DROP SEQUENCE IF EXISTS talkgroups_id_seq; + +ALTER TABLE talkgroups ADD COLUMN IF NOT EXISTS learned BOOLEAN NOT NULL DEFAULT FALSE; + +-- calls fkey constraint requires us to migrate all calls' talkgroup tuples to exist +INSERT INTO talkgroups (system_id, tgid, learned) +SELECT DISTINCT system_id, tgid, TRUE FROM talkgroups_learned ON CONFLICT DO NOTHING; + +INSERT INTO talkgroups (system_id, tgid, learned) +SELECT DISTINCT system, talkgroup, TRUE FROM calls ON CONFLICT DO NOTHING; + +INSERT INTO talkgroups_learned (system_id, tgid, name, tg_group, alpha_tag) +SELECT DISTINCT c.system, c.talkgroup, c.tg_label, c.tg_group, c.tg_alpha_tag +FROM calls c +JOIN talkgroups t ON (t.system_id = c.system AND t.tgid = c.talkgroup AND t.learned IS TRUE) +ON CONFLICT DO NOTHING; + +ALTER TABLE calls ADD CONSTRAINT calls_system_talkgroup_fkey FOREIGN KEY (system, talkgroup) REFERENCES talkgroups(system_id, tgid); diff --git a/sql/postgres/migrations/initial.sql b/sql/postgres/migrations/initial.sql new file mode 100644 index 0000000..445fa4f --- /dev/null +++ b/sql/postgres/migrations/initial.sql @@ -0,0 +1,121 @@ +CREATE TABLE IF NOT EXISTS users( + id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + username VARCHAR (255) UNIQUE NOT NULL, + password TEXT NOT NULL, + email TEXT NOT NULL, + is_admin BOOLEAN NOT NULL, + prefs JSONB +); + +CREATE INDEX IF NOT EXISTS users_username_idx ON users(username); + +CREATE TABLE IF NOT EXISTS api_keys( + id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + owner INTEGER REFERENCES users(id) NOT NULL, + created_at TIMESTAMP NOT NULL, + expires TIMESTAMP, + disabled BOOLEAN, + api_key TEXT UNIQUE NOT NULL +); + +CREATE TABLE IF NOT EXISTS systems( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL +); + +CREATE TABLE IF NOT EXISTS talkgroups( + id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + system_id INT4 REFERENCES systems(id) NOT NULL, + tgid INT4 NOT NULL, + name TEXT, + alpha_tag TEXT, + tg_group TEXT, + frequency INTEGER, + metadata JSONB, + tags TEXT[] NOT NULL DEFAULT '{}', + alert BOOLEAN NOT NULL DEFAULT 'true', + alert_config JSONB, + weight REAL NOT NULL DEFAULT 1.0, + learned BOOLEAN, + UNIQUE (system_id, tgid) +); + +CREATE INDEX talkgroups_system_tgid_idx ON talkgroups (system_id, tgid); + +CREATE INDEX IF NOT EXISTS talkgroup_id_tags ON talkgroups USING GIN (tags); + +CREATE TABLE IF NOT EXISTS talkgroups_learned( + id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + system_id INTEGER REFERENCES systems(id) NOT NULL, + tgid INTEGER NOT NULL, + name TEXT NOT NULL, + alpha_tag TEXT, + tg_group TEXT, + ignored BOOLEAN, + UNIQUE (system_id, tgid, name) +); + +CREATE TABLE IF NOT EXISTS alerts( + id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + time TIMESTAMPTZ NOT NULL, + tgid INTEGER NOT NULL, + system_id INTEGER REFERENCES systems(id) NOT NULL, + weight REAL, + score REAL, + orig_score REAL, + notified BOOLEAN NOT NULL DEFAULT 'false', + metadata JSONB +); + +CREATE TABLE IF NOT EXISTS calls( + id UUID PRIMARY KEY, + submitter INTEGER REFERENCES api_keys(id) ON DELETE SET NULL, + system INTEGER NOT NULL, + talkgroup INTEGER NOT NULL, + call_date TIMESTAMPTZ NOT NULL, + audio_name TEXT, + audio_blob BYTEA, + duration INTEGER, + audio_type TEXT, + audio_url TEXT, + frequency INTEGER NOT NULL, + frequencies INTEGER[], + patches INTEGER[], + tg_label TEXT, + tg_alpha_tag TEXT, + tg_group TEXT, + source INTEGER NOT NULL, + transcript TEXT, + FOREIGN KEY (system, talkgroup) REFERENCES talkgroups(system_id, tgid) +); + +CREATE INDEX IF NOT EXISTS calls_transcript_idx ON calls USING GIN (to_tsvector('english', transcript)); +CREATE INDEX IF NOT EXISTS calls_call_date_tg_idx ON calls(system, talkgroup, call_date); + +CREATE TABLE IF NOT EXISTS settings( + name TEXT PRIMARY KEY, + updated_by INTEGER REFERENCES users(id), + value JSONB +); + +CREATE TABLE IF NOT EXISTS incidents( + id UUID PRIMARY KEY, + name TEXT NOT NULL, + description TEXT, + start_time TIMESTAMP, + end_time TIMESTAMP, + location JSONB, + metadata JSONB +); + +CREATE INDEX IF NOT EXISTS incidents_name_description_idx ON incidents USING GIN ( + (to_tsvector('english', name) || to_tsvector('english', coalesce(description, '')) + ) +); + +CREATE TABLE IF NOT EXISTS incidents_calls( + incident_id UUID REFERENCES incidents(id) ON UPDATE CASCADE ON DELETE CASCADE, + call_id UUID REFERENCES calls(id) ON UPDATE CASCADE, + notes JSONB, + PRIMARY KEY (incident_id, call_id) +); diff --git a/sql/postgres/queries/calls.sql b/sql/postgres/queries/calls.sql index 1cb09fe..5253556 100644 --- a/sql/postgres/queries/calls.sql +++ b/sql/postgres/queries/calls.sql @@ -41,10 +41,9 @@ source UPDATE calls SET transcript = $2 WHERE id = $1; -- name: AddAlert :exec -INSERT INTO alerts (id, time, tgid, system_id, weight, score, orig_score, notified, metadata) +INSERT INTO alerts (time, tgid, system_id, weight, score, orig_score, notified, metadata) VALUES ( - sqlc.arg(id), sqlc.arg(time), sqlc.arg(tgid), sqlc.arg(system_id), diff --git a/sql/postgres/queries/talkgroups.sql b/sql/postgres/queries/talkgroups.sql index 6b58ae8..82a9be9 100644 --- a/sql/postgres/queries/talkgroups.sql +++ b/sql/postgres/queries/talkgroups.sql @@ -26,53 +26,48 @@ WHERE (system_id, tgid) = (@system_id, @tg_id); -- name: GetTalkgroupWithLearned :one SELECT -sqlc.embed(tg), sqlc.embed(sys), -FALSE learned +sqlc.embed(tg), sqlc.embed(sys) FROM talkgroups tg JOIN systems sys ON tg.system_id = sys.id -WHERE (tg.system_id, tg.tgid) = (@system_id, @tgid) +WHERE (tg.system_id, tg.tgid) = (@system_id, @tgid) AND tg.learned IS NOT TRUE UNION SELECT tgl.id, tgl.system_id::INT4, tgl.tgid::INT4, tgl.name, -tgl.alpha_tag, tgl.alpha_tag, NULL::INTEGER, NULL::JSONB, -CASE WHEN tgl.alpha_tag IS NULL THEN NULL ELSE ARRAY[tgl.alpha_tag] END, -TRUE, NULL::JSONB, 1.0, sys.id, sys.name, -TRUE learned +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, +TRUE, 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 SELECT -sqlc.embed(tg), sqlc.embed(sys), -FALSE learned +sqlc.embed(tg), sqlc.embed(sys) FROM talkgroups tg JOIN systems sys ON tg.system_id = sys.id -WHERE tg.system_id = @system +WHERE tg.system_id = @system AND tg.learned IS NOT TRUE UNION SELECT tgl.id, tgl.system_id::INT4, tgl.tgid::INT4, tgl.name, -tgl.alpha_tag, tgl.alpha_tag, NULL::INTEGER, NULL::JSONB, -CASE WHEN tgl.alpha_tag IS NULL THEN NULL ELSE ARRAY[tgl.alpha_tag] END, -TRUE, NULL::JSONB, 1.0, sys.id, sys.name, -TRUE learned +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, +TRUE, 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 SELECT -sqlc.embed(tg), sqlc.embed(sys), -FALSE learned +sqlc.embed(tg), sqlc.embed(sys) FROM talkgroups tg 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.alpha_tag, NULL::INTEGER, NULL::JSONB, -CASE WHEN tgl.alpha_tag IS NULL THEN NULL ELSE ARRAY[tgl.alpha_tag] END, -TRUE, NULL::JSONB, 1.0, sys.id, sys.name, -TRUE learned +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, +TRUE, 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; @@ -94,3 +89,29 @@ SET weight = COALESCE(sqlc.narg('weight'), weight) WHERE id = sqlc.narg('id') OR (system_id = sqlc.narg('system_id') AND tgid = sqlc.narg('tgid')) RETURNING *; + +-- name: AddTalkgroupWithLearnedFlag :exec +INSERT INTO talkgroups ( + system_id, + tgid, + learned +) VALUES( + @system_id, + @tgid, + 't' +); + +-- name: AddLearnedTalkgroup :one +INSERT INTO talkgroups_learned( + system_id, + tgid, + name, + alpha_tag, + tg_group +) VALUES ( + @system_id, + @tgid, + sqlc.narg('name'), + sqlc.narg('alpha_tag'), + sqlc.narg('tg_group') +) RETURNING id; diff --git a/sql/sqlc.yaml b/sql/sqlc.yaml index 506170a..4ce8915 100644 --- a/sql/sqlc.yaml +++ b/sql/sqlc.yaml @@ -14,6 +14,7 @@ sql: initialisms: - id - tgid + - tg emit_pointers_for_null_types: true overrides: - db_type: "uuid" From 78f6df5920c815aac73e199d697b75f8c045d845 Mon Sep 17 00:00:00 2001 From: Daniel Ponte Date: Tue, 19 Nov 2024 08:50:01 -0500 Subject: [PATCH 2/5] Flatten migrations --- sql/postgres/migrations/001_initial.up.sql | 10 +- .../migrations/002_tglearned.down.sql | 25 ---- sql/postgres/migrations/002_tglearned.up.sql | 24 ---- sql/postgres/migrations/initial.sql | 121 ------------------ 4 files changed, 6 insertions(+), 174 deletions(-) delete mode 100644 sql/postgres/migrations/002_tglearned.down.sql delete mode 100644 sql/postgres/migrations/002_tglearned.up.sql delete mode 100644 sql/postgres/migrations/initial.sql diff --git a/sql/postgres/migrations/001_initial.up.sql b/sql/postgres/migrations/001_initial.up.sql index 40d49ee..445fa4f 100644 --- a/sql/postgres/migrations/001_initial.up.sql +++ b/sql/postgres/migrations/001_initial.up.sql @@ -1,5 +1,5 @@ CREATE TABLE IF NOT EXISTS users( - id SERIAL PRIMARY KEY, + id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, username VARCHAR (255) UNIQUE NOT NULL, password TEXT NOT NULL, email TEXT NOT NULL, @@ -10,7 +10,7 @@ CREATE TABLE IF NOT EXISTS users( CREATE INDEX IF NOT EXISTS users_username_idx ON users(username); CREATE TABLE IF NOT EXISTS api_keys( - id SERIAL PRIMARY KEY, + id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, owner INTEGER REFERENCES users(id) NOT NULL, created_at TIMESTAMP NOT NULL, expires TIMESTAMP, @@ -24,7 +24,7 @@ CREATE TABLE IF NOT EXISTS systems( ); CREATE TABLE IF NOT EXISTS talkgroups( - id UUID PRIMARY KEY, + id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, system_id INT4 REFERENCES systems(id) NOT NULL, tgid INT4 NOT NULL, name TEXT, @@ -36,6 +36,7 @@ CREATE TABLE IF NOT EXISTS talkgroups( alert BOOLEAN NOT NULL DEFAULT 'true', alert_config JSONB, weight REAL NOT NULL DEFAULT 1.0, + learned BOOLEAN, UNIQUE (system_id, tgid) ); @@ -84,7 +85,8 @@ CREATE TABLE IF NOT EXISTS calls( tg_alpha_tag TEXT, tg_group TEXT, source INTEGER NOT NULL, - transcript TEXT + transcript TEXT, + FOREIGN KEY (system, talkgroup) REFERENCES talkgroups(system_id, tgid) ); CREATE INDEX IF NOT EXISTS calls_transcript_idx ON calls USING GIN (to_tsvector('english', transcript)); diff --git a/sql/postgres/migrations/002_tglearned.down.sql b/sql/postgres/migrations/002_tglearned.down.sql deleted file mode 100644 index 7355bf1..0000000 --- a/sql/postgres/migrations/002_tglearned.down.sql +++ /dev/null @@ -1,25 +0,0 @@ -ALTER TABLE calls DROP CONSTRAINT IF EXISTS calls_system_talkgroup_fkey; - -ALTER TABLE talkgroups DROP COLUMN IF EXISTS learned; - -ALTER TABLE talkgroups ALTER COLUMN id DROP IDENTITY IF EXISTS; -ALTER TABLE talkgroups ALTER COLUMN id SET DATA TYPE UUID USING (gen_random_uuid()); -DROP SEQUENCE IF EXISTS talkgroups_id_seq; - -CREATE OR REPLACE FUNCTION learn_talkgroup() -RETURNS TRIGGER AS $$ -BEGIN - IF NOT EXISTS ( - SELECT tg.system_id, tg.tgid, tg.name, tg.alpha_tag FROM talkgroups tg WHERE tg.system_id = NEW.system AND tg.tgid = NEW.talkgroup - UNION - SELECT tgl.system_id, tgl.tgid, tgl.name, tgl.alpha_tag FROM talkgroups_learned tgl WHERE tgl.system_id = NEW.system AND tgl.tgid = NEW.talkgroup - ) THEN - INSERT INTO talkgroups_learned(system_id, tgid, name, alpha_tag) VALUES( - NEW.system, NEW.talkgroup, NEW.tg_label, NEW.tg_alpha_tag - ) ON CONFLICT DO NOTHING; - END IF; - RETURN NEW; -END -$$ LANGUAGE plpgsql; - -CREATE OR REPLACE TRIGGER learn_tg AFTER INSERT ON calls FOR EACH ROW EXECUTE FUNCTION learn_talkgroup(); diff --git a/sql/postgres/migrations/002_tglearned.up.sql b/sql/postgres/migrations/002_tglearned.up.sql deleted file mode 100644 index 21f5cd2..0000000 --- a/sql/postgres/migrations/002_tglearned.up.sql +++ /dev/null @@ -1,24 +0,0 @@ -DROP TRIGGER IF EXISTS learn_tg ON calls; -DROP FUNCTION IF EXISTS learn_talkgroup(); - -CREATE SEQUENCE IF NOT EXISTS talkgroups_id_seq START WITH 1; -ALTER TABLE talkgroups ALTER COLUMN id SET DATA TYPE INTEGER USING (nextval('talkgroups_id_seq')); -ALTER TABLE talkgroups ALTER COLUMN id ADD GENERATED ALWAYS AS IDENTITY; -DROP SEQUENCE IF EXISTS talkgroups_id_seq; - -ALTER TABLE talkgroups ADD COLUMN IF NOT EXISTS learned BOOLEAN NOT NULL DEFAULT FALSE; - --- calls fkey constraint requires us to migrate all calls' talkgroup tuples to exist -INSERT INTO talkgroups (system_id, tgid, learned) -SELECT DISTINCT system_id, tgid, TRUE FROM talkgroups_learned ON CONFLICT DO NOTHING; - -INSERT INTO talkgroups (system_id, tgid, learned) -SELECT DISTINCT system, talkgroup, TRUE FROM calls ON CONFLICT DO NOTHING; - -INSERT INTO talkgroups_learned (system_id, tgid, name, tg_group, alpha_tag) -SELECT DISTINCT c.system, c.talkgroup, c.tg_label, c.tg_group, c.tg_alpha_tag -FROM calls c -JOIN talkgroups t ON (t.system_id = c.system AND t.tgid = c.talkgroup AND t.learned IS TRUE) -ON CONFLICT DO NOTHING; - -ALTER TABLE calls ADD CONSTRAINT calls_system_talkgroup_fkey FOREIGN KEY (system, talkgroup) REFERENCES talkgroups(system_id, tgid); diff --git a/sql/postgres/migrations/initial.sql b/sql/postgres/migrations/initial.sql deleted file mode 100644 index 445fa4f..0000000 --- a/sql/postgres/migrations/initial.sql +++ /dev/null @@ -1,121 +0,0 @@ -CREATE TABLE IF NOT EXISTS users( - id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - username VARCHAR (255) UNIQUE NOT NULL, - password TEXT NOT NULL, - email TEXT NOT NULL, - is_admin BOOLEAN NOT NULL, - prefs JSONB -); - -CREATE INDEX IF NOT EXISTS users_username_idx ON users(username); - -CREATE TABLE IF NOT EXISTS api_keys( - id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - owner INTEGER REFERENCES users(id) NOT NULL, - created_at TIMESTAMP NOT NULL, - expires TIMESTAMP, - disabled BOOLEAN, - api_key TEXT UNIQUE NOT NULL -); - -CREATE TABLE IF NOT EXISTS systems( - id INTEGER PRIMARY KEY, - name TEXT NOT NULL -); - -CREATE TABLE IF NOT EXISTS talkgroups( - id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - system_id INT4 REFERENCES systems(id) NOT NULL, - tgid INT4 NOT NULL, - name TEXT, - alpha_tag TEXT, - tg_group TEXT, - frequency INTEGER, - metadata JSONB, - tags TEXT[] NOT NULL DEFAULT '{}', - alert BOOLEAN NOT NULL DEFAULT 'true', - alert_config JSONB, - weight REAL NOT NULL DEFAULT 1.0, - learned BOOLEAN, - UNIQUE (system_id, tgid) -); - -CREATE INDEX talkgroups_system_tgid_idx ON talkgroups (system_id, tgid); - -CREATE INDEX IF NOT EXISTS talkgroup_id_tags ON talkgroups USING GIN (tags); - -CREATE TABLE IF NOT EXISTS talkgroups_learned( - id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - system_id INTEGER REFERENCES systems(id) NOT NULL, - tgid INTEGER NOT NULL, - name TEXT NOT NULL, - alpha_tag TEXT, - tg_group TEXT, - ignored BOOLEAN, - UNIQUE (system_id, tgid, name) -); - -CREATE TABLE IF NOT EXISTS alerts( - id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - time TIMESTAMPTZ NOT NULL, - tgid INTEGER NOT NULL, - system_id INTEGER REFERENCES systems(id) NOT NULL, - weight REAL, - score REAL, - orig_score REAL, - notified BOOLEAN NOT NULL DEFAULT 'false', - metadata JSONB -); - -CREATE TABLE IF NOT EXISTS calls( - id UUID PRIMARY KEY, - submitter INTEGER REFERENCES api_keys(id) ON DELETE SET NULL, - system INTEGER NOT NULL, - talkgroup INTEGER NOT NULL, - call_date TIMESTAMPTZ NOT NULL, - audio_name TEXT, - audio_blob BYTEA, - duration INTEGER, - audio_type TEXT, - audio_url TEXT, - frequency INTEGER NOT NULL, - frequencies INTEGER[], - patches INTEGER[], - tg_label TEXT, - tg_alpha_tag TEXT, - tg_group TEXT, - source INTEGER NOT NULL, - transcript TEXT, - FOREIGN KEY (system, talkgroup) REFERENCES talkgroups(system_id, tgid) -); - -CREATE INDEX IF NOT EXISTS calls_transcript_idx ON calls USING GIN (to_tsvector('english', transcript)); -CREATE INDEX IF NOT EXISTS calls_call_date_tg_idx ON calls(system, talkgroup, call_date); - -CREATE TABLE IF NOT EXISTS settings( - name TEXT PRIMARY KEY, - updated_by INTEGER REFERENCES users(id), - value JSONB -); - -CREATE TABLE IF NOT EXISTS incidents( - id UUID PRIMARY KEY, - name TEXT NOT NULL, - description TEXT, - start_time TIMESTAMP, - end_time TIMESTAMP, - location JSONB, - metadata JSONB -); - -CREATE INDEX IF NOT EXISTS incidents_name_description_idx ON incidents USING GIN ( - (to_tsvector('english', name) || to_tsvector('english', coalesce(description, '')) - ) -); - -CREATE TABLE IF NOT EXISTS incidents_calls( - incident_id UUID REFERENCES incidents(id) ON UPDATE CASCADE ON DELETE CASCADE, - call_id UUID REFERENCES calls(id) ON UPDATE CASCADE, - notes JSONB, - PRIMARY KEY (incident_id, call_id) -); From f6cb8b4d1705f649843b291579dc1b872f2fa9bb Mon Sep 17 00:00:00 2001 From: Daniel Ponte Date: Tue, 19 Nov 2024 09:10:55 -0500 Subject: [PATCH 3/5] fixes --- pkg/auth/jwt.go | 4 +-- pkg/database/mocks/Store.go | 32 +++++++++++----------- pkg/database/models.go | 4 +-- pkg/database/querier.go | 4 +-- pkg/database/users.sql.go | 4 +-- pkg/sinks/relay.go | 8 +++--- sql/postgres/migrations/001_initial.up.sql | 2 +- 7 files changed, 29 insertions(+), 29 deletions(-) diff --git a/pkg/auth/jwt.go b/pkg/auth/jwt.go index d85bbb8..c915c29 100644 --- a/pkg/auth/jwt.go +++ b/pkg/auth/jwt.go @@ -98,7 +98,7 @@ func (a *Auth) Login(ctx context.Context, username, password string) (token stri return a.newToken(found.ID), nil } -func (a *Auth) newToken(uid int32) string { +func (a *Auth) newToken(uid int) string { claims := claims{ "sub": strconv.Itoa(int(uid)), } @@ -134,7 +134,7 @@ func (a *Auth) routeRefresh(w http.ResponseWriter, r *http.Request) { return } - tok := a.newToken(int32(uid)) + tok := a.newToken(uid) cookie := &http.Cookie{ Name: "jwt", diff --git a/pkg/database/mocks/Store.go b/pkg/database/mocks/Store.go index 5ea8209..2c769a4 100644 --- a/pkg/database/mocks/Store.go +++ b/pkg/database/mocks/Store.go @@ -1293,7 +1293,7 @@ func (_c *Store_GetTalkgroupsWithLearnedBySystem_Call) RunAndReturn(run func(con } // GetUserByID provides a mock function with given fields: ctx, id -func (_m *Store) GetUserByID(ctx context.Context, id int32) (database.User, error) { +func (_m *Store) GetUserByID(ctx context.Context, id int) (database.User, error) { ret := _m.Called(ctx, id) if len(ret) == 0 { @@ -1302,16 +1302,16 @@ func (_m *Store) GetUserByID(ctx context.Context, id int32) (database.User, erro var r0 database.User var r1 error - if rf, ok := ret.Get(0).(func(context.Context, int32) (database.User, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, int) (database.User, error)); ok { return rf(ctx, id) } - if rf, ok := ret.Get(0).(func(context.Context, int32) database.User); ok { + if rf, ok := ret.Get(0).(func(context.Context, int) database.User); ok { r0 = rf(ctx, id) } else { r0 = ret.Get(0).(database.User) } - if rf, ok := ret.Get(1).(func(context.Context, int32) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, id) } else { r1 = ret.Error(1) @@ -1327,14 +1327,14 @@ type Store_GetUserByID_Call struct { // GetUserByID is a helper method to define mock.On call // - ctx context.Context -// - id int32 +// - id int func (_e *Store_Expecter) GetUserByID(ctx interface{}, id interface{}) *Store_GetUserByID_Call { return &Store_GetUserByID_Call{Call: _e.mock.On("GetUserByID", ctx, id)} } -func (_c *Store_GetUserByID_Call) Run(run func(ctx context.Context, id int32)) *Store_GetUserByID_Call { +func (_c *Store_GetUserByID_Call) Run(run func(ctx context.Context, id int)) *Store_GetUserByID_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(int32)) + run(args[0].(context.Context), args[1].(int)) }) return _c } @@ -1344,13 +1344,13 @@ func (_c *Store_GetUserByID_Call) Return(_a0 database.User, _a1 error) *Store_Ge return _c } -func (_c *Store_GetUserByID_Call) RunAndReturn(run func(context.Context, int32) (database.User, error)) *Store_GetUserByID_Call { +func (_c *Store_GetUserByID_Call) RunAndReturn(run func(context.Context, int) (database.User, error)) *Store_GetUserByID_Call { _c.Call.Return(run) return _c } // GetUserByUID provides a mock function with given fields: ctx, id -func (_m *Store) GetUserByUID(ctx context.Context, id int32) (database.User, error) { +func (_m *Store) GetUserByUID(ctx context.Context, id int) (database.User, error) { ret := _m.Called(ctx, id) if len(ret) == 0 { @@ -1359,16 +1359,16 @@ func (_m *Store) GetUserByUID(ctx context.Context, id int32) (database.User, err var r0 database.User var r1 error - if rf, ok := ret.Get(0).(func(context.Context, int32) (database.User, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, int) (database.User, error)); ok { return rf(ctx, id) } - if rf, ok := ret.Get(0).(func(context.Context, int32) database.User); ok { + if rf, ok := ret.Get(0).(func(context.Context, int) database.User); ok { r0 = rf(ctx, id) } else { r0 = ret.Get(0).(database.User) } - if rf, ok := ret.Get(1).(func(context.Context, int32) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { r1 = rf(ctx, id) } else { r1 = ret.Error(1) @@ -1384,14 +1384,14 @@ type Store_GetUserByUID_Call struct { // GetUserByUID is a helper method to define mock.On call // - ctx context.Context -// - id int32 +// - id int func (_e *Store_Expecter) GetUserByUID(ctx interface{}, id interface{}) *Store_GetUserByUID_Call { return &Store_GetUserByUID_Call{Call: _e.mock.On("GetUserByUID", ctx, id)} } -func (_c *Store_GetUserByUID_Call) Run(run func(ctx context.Context, id int32)) *Store_GetUserByUID_Call { +func (_c *Store_GetUserByUID_Call) Run(run func(ctx context.Context, id int)) *Store_GetUserByUID_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(int32)) + run(args[0].(context.Context), args[1].(int)) }) return _c } @@ -1401,7 +1401,7 @@ func (_c *Store_GetUserByUID_Call) Return(_a0 database.User, _a1 error) *Store_G return _c } -func (_c *Store_GetUserByUID_Call) RunAndReturn(run func(context.Context, int32) (database.User, error)) *Store_GetUserByUID_Call { +func (_c *Store_GetUserByUID_Call) RunAndReturn(run func(context.Context, int) (database.User, error)) *Store_GetUserByUID_Call { _c.Call.Return(run) return _c } diff --git a/pkg/database/models.go b/pkg/database/models.go index 987df9c..f756897 100644 --- a/pkg/database/models.go +++ b/pkg/database/models.go @@ -26,7 +26,7 @@ type Alert struct { } type ApiKey struct { - ID int32 `json:"id"` + ID int `json:"id"` Owner int `json:"owner"` CreatedAt time.Time `json:"created_at"` Expires pgtype.Timestamp `json:"expires"` @@ -109,7 +109,7 @@ type TalkgroupsLearned struct { } type User struct { - ID int32 `json:"id"` + ID int `json:"id"` Username string `json:"username"` Password string `json:"password"` Email string `json:"email"` diff --git a/pkg/database/querier.go b/pkg/database/querier.go index d1358a4..aa42757 100644 --- a/pkg/database/querier.go +++ b/pkg/database/querier.go @@ -31,8 +31,8 @@ type Querier interface { GetTalkgroupsWithAnyTags(ctx context.Context, tags []string) ([]GetTalkgroupsWithAnyTagsRow, error) GetTalkgroupsWithLearned(ctx context.Context) ([]GetTalkgroupsWithLearnedRow, error) GetTalkgroupsWithLearnedBySystem(ctx context.Context, system int32) ([]GetTalkgroupsWithLearnedBySystemRow, error) - GetUserByID(ctx context.Context, id int32) (User, error) - GetUserByUID(ctx context.Context, id int32) (User, error) + GetUserByID(ctx context.Context, id int) (User, error) + GetUserByUID(ctx context.Context, id int) (User, error) GetUserByUsername(ctx context.Context, username string) (User, error) GetUsers(ctx context.Context) ([]User, error) SetCallTranscript(ctx context.Context, iD uuid.UUID, transcript *string) error diff --git a/pkg/database/users.sql.go b/pkg/database/users.sql.go index 479cbc8..fa05dff 100644 --- a/pkg/database/users.sql.go +++ b/pkg/database/users.sql.go @@ -113,7 +113,7 @@ SELECT id, username, password, email, is_admin, prefs FROM users WHERE id = $1 LIMIT 1 ` -func (q *Queries) GetUserByID(ctx context.Context, id int32) (User, error) { +func (q *Queries) GetUserByID(ctx context.Context, id int) (User, error) { row := q.db.QueryRow(ctx, getUserByID, id) var i User err := row.Scan( @@ -132,7 +132,7 @@ SELECT id, username, password, email, is_admin, prefs FROM users WHERE id = $1 LIMIT 1 ` -func (q *Queries) GetUserByUID(ctx context.Context, id int32) (User, error) { +func (q *Queries) GetUserByUID(ctx context.Context, id int) (User, error) { row := q.db.QueryRow(ctx, getUserByUID, id) var i User err := row.Scan( diff --git a/pkg/sinks/relay.go b/pkg/sinks/relay.go index 1f3bb97..a481764 100644 --- a/pkg/sinks/relay.go +++ b/pkg/sinks/relay.go @@ -44,21 +44,20 @@ func NewRelayManager(s Sinks, cfgs []config.Relay) (*RelayManager, error) { } for i, cfg := range cfgs { - rs, err := rm.newRelay(cfg) + rs, err := rm.newRelay(i, cfg) if err != nil { return nil, err } rm.relays = append(rm.relays, rs) - sinkName := fmt.Sprintf("relay%d:%s", i, rs.url.Host) - s.Register(sinkName, rs, cfg.Required) + s.Register(rs.Name, rs, cfg.Required) } return rm, nil } -func (rs *RelayManager) newRelay(cfg config.Relay) (*Relay, error) { +func (rs *RelayManager) newRelay(idx int, cfg config.Relay) (*Relay, error) { u, err := url.Parse(cfg.URL) if err != nil { return nil, err @@ -71,6 +70,7 @@ func (rs *RelayManager) newRelay(cfg config.Relay) (*Relay, error) { u = u.JoinPath("/api/call-upload") return &Relay{ + Name: fmt.Sprintf("relay%d:%s", idx, u.Host), Relay: cfg, url: u, mgr: rs, diff --git a/sql/postgres/migrations/001_initial.up.sql b/sql/postgres/migrations/001_initial.up.sql index 445fa4f..59aa9ec 100644 --- a/sql/postgres/migrations/001_initial.up.sql +++ b/sql/postgres/migrations/001_initial.up.sql @@ -36,7 +36,7 @@ CREATE TABLE IF NOT EXISTS talkgroups( alert BOOLEAN NOT NULL DEFAULT 'true', alert_config JSONB, weight REAL NOT NULL DEFAULT 1.0, - learned BOOLEAN, + learned BOOLEAN NOT NULL DEFAULT FALSE, UNIQUE (system_id, tgid) ); From 711c8fbad06301d243d1cada0fedf9a270bc1d51 Mon Sep 17 00:00:00 2001 From: Daniel Ponte Date: Tue, 19 Nov 2024 09:55:57 -0500 Subject: [PATCH 4/5] tests --- pkg/database/talkgroups.sql.go | 2 +- pkg/database/talkgroups.sql_test.go | 49 +++++++++----------- pkg/talkgroups/importer/import_test.go | 2 +- pkg/talkgroups/importer/testdata/riscon.json | 2 +- sql/sqlc.yaml | 4 -- 5 files changed, 25 insertions(+), 34 deletions(-) diff --git a/pkg/database/talkgroups.sql.go b/pkg/database/talkgroups.sql.go index 9291314..5ee4fc3 100644 --- a/pkg/database/talkgroups.sql.go +++ b/pkg/database/talkgroups.sql.go @@ -434,7 +434,7 @@ type UpdateTalkgroupParams struct { Alert *bool `json:"alert"` AlertConfig rules.AlertRules `json:"alert_config"` Weight *float32 `json:"weight"` - ID *int32 `json:"id"` + ID int `json:"id"` SystemID *int32 `json:"system_id"` TGID *int32 `json:"tgid"` } diff --git a/pkg/database/talkgroups.sql_test.go b/pkg/database/talkgroups.sql_test.go index 14c9215..830c85b 100644 --- a/pkg/database/talkgroups.sql_test.go +++ b/pkg/database/talkgroups.sql_test.go @@ -3,23 +3,21 @@ package database import ( "testing" - "github.com/stretchr/testify/require" + "github.com/stretchr/testify/assert" ) const getTalkgroupWithLearnedTest = `-- name: GetTalkgroupWithLearned :one SELECT -tg.id, 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, sys.id, sys.name, -FALSE learned +tg.id, 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, sys.id, sys.name FROM talkgroups tg JOIN systems sys ON tg.system_id = sys.id -WHERE (tg.system_id, tg.tgid) = ($1, $2) +WHERE (tg.system_id, tg.tgid) = ($1, $2) AND tg.learned IS NOT TRUE UNION SELECT -NULL::UUID, tgl.system_id::INT4, tgl.tgid::INT4, tgl.name, -tgl.alpha_tag, tgl.alpha_tag, NULL::INTEGER, NULL::JSONB, -CASE WHEN tgl.alpha_tag IS NULL THEN NULL ELSE ARRAY[tgl.alpha_tag] END, -TRUE, NULL::JSONB, 1.0, sys.id, sys.name, -TRUE learned +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, +TRUE, 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 = $1 AND tgl.tgid = $2 AND ignored IS NOT TRUE @@ -27,18 +25,16 @@ WHERE tgl.system_id = $1 AND tgl.tgid = $2 AND ignored IS NOT TRUE const getTalkgroupsWithLearnedBySystemTest = `-- name: GetTalkgroupsWithLearnedBySystem :many SELECT -tg.id, 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, sys.id, sys.name, -FALSE learned +tg.id, 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, sys.id, sys.name FROM talkgroups tg JOIN systems sys ON tg.system_id = sys.id -WHERE tg.system_id = $1 +WHERE tg.system_id = $1 AND tg.learned IS NOT TRUE UNION SELECT -NULL::UUID, tgl.system_id::INT4, tgl.tgid::INT4, tgl.name, -tgl.alpha_tag, tgl.alpha_tag, NULL::INTEGER, NULL::JSONB, -CASE WHEN tgl.alpha_tag IS NULL THEN NULL ELSE ARRAY[tgl.alpha_tag] END, -TRUE, NULL::JSONB, 1.0, sys.id, sys.name, -TRUE learned +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, +TRUE, 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 = $1 AND ignored IS NOT TRUE @@ -46,24 +42,23 @@ WHERE tgl.system_id = $1 AND ignored IS NOT TRUE const getTalkgroupsWithLearnedTest = `-- name: GetTalkgroupsWithLearned :many SELECT -tg.id, 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, sys.id, sys.name, -FALSE learned +tg.id, 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, sys.id, sys.name FROM talkgroups tg JOIN systems sys ON tg.system_id = sys.id +WHERE tg.learned IS NOT TRUE UNION SELECT -NULL::UUID, tgl.system_id::INT4, tgl.tgid::INT4, tgl.name, -tgl.alpha_tag, tgl.alpha_tag, NULL::INTEGER, NULL::JSONB, -CASE WHEN tgl.alpha_tag IS NULL THEN NULL ELSE ARRAY[tgl.alpha_tag] END, -TRUE, NULL::JSONB, 1.0, sys.id, sys.name, -TRUE learned +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, +TRUE, 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 ` func TestQueryColumnsMatch(t *testing.T) { - require.Equal(t, getTalkgroupWithLearnedTest, getTalkgroupWithLearned) - require.Equal(t, getTalkgroupsWithLearnedBySystemTest, getTalkgroupsWithLearnedBySystem) - require.Equal(t, getTalkgroupsWithLearnedTest, getTalkgroupsWithLearned) + assert.Equal(t, getTalkgroupWithLearnedTest, getTalkgroupWithLearned) + assert.Equal(t, getTalkgroupsWithLearnedBySystemTest, getTalkgroupsWithLearnedBySystem) + assert.Equal(t, getTalkgroupsWithLearnedTest, getTalkgroupsWithLearned) } diff --git a/pkg/talkgroups/importer/import_test.go b/pkg/talkgroups/importer/import_test.go index 9957f47..980d5e9 100644 --- a/pkg/talkgroups/importer/import_test.go +++ b/pkg/talkgroups/importer/import_test.go @@ -57,7 +57,7 @@ func TestImport(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - dbMock := mocks.NewDB(t) + dbMock := mocks.NewStore(t) if tc.expectErr == nil { dbMock.EXPECT().GetSystemName(mock.AnythingOfType("*context.valueCtx"), tc.sysID).Return(tc.sysName, nil) } diff --git a/pkg/talkgroups/importer/testdata/riscon.json b/pkg/talkgroups/importer/testdata/riscon.json index 5a0c63e..3ebb955 100644 --- a/pkg/talkgroups/importer/testdata/riscon.json +++ b/pkg/talkgroups/importer/testdata/riscon.json @@ -1 +1 @@ -[{"id":"52fdfc07-2182-454f-963f-5f0f9a621d72","system_id":197,"tgid":2,"name":"Intercity Fire","alpha_tag":"Intercity FD","tg_group":"Statewide Mutual Aid/Intersystem","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"9566c74d-1003-4c4d-bbbb-0407d1e2c649","system_id":197,"tgid":3,"name":"Intercity Police","alpha_tag":"Intercity PD","tg_group":"Statewide Mutual Aid/Intersystem","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"81855ad8-681d-4d86-91e9-1e00167939cb","system_id":197,"tgid":21,"name":"North Dispatch ","alpha_tag":"RISP N Disp","tg_group":"State Police - District A (North)","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"6694d2c4-22ac-4208-a007-2939487f6999","system_id":197,"tgid":22,"name":"North Car-to-Car/Information","alpha_tag":"RISP N Car","tg_group":"State Police - District A (North)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"eb9d18a4-4784-445d-87f3-c67cf22746e9","system_id":197,"tgid":24,"name":"North Tactical Ops 1","alpha_tag":"RISP N Tac 1","tg_group":"State Police - District A (North)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"95af5a25-3679-41ba-a2ff-6cd471c483f1","system_id":197,"tgid":23,"name":"North Tactical Ops 2","alpha_tag":"RISP N Tac 2","tg_group":"State Police - District A (North)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"5fb90bad-b37c-4821-b6d9-5526a41a9504","system_id":197,"tgid":25,"name":"South Dispatch ","alpha_tag":"RISP S Disp","tg_group":"State Police - District B (South)","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"680b4e7c-8b76-4a1b-9d49-d4955c848621","system_id":197,"tgid":27,"name":"South Car-to-Car/Information","alpha_tag":"RISP S Car","tg_group":"State Police - District B (South)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"6325253f-ec73-4dd7-a9e2-8bf921119c16","system_id":197,"tgid":16,"name":"State Fire Marshall","alpha_tag":"State FMO","tg_group":"Statewide Fire","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"0f070244-8615-4bda-8831-3f6a8eb668d2","system_id":197,"tgid":1038,"name":"Northern Rhode Island Fire Chiefs","alpha_tag":"NRI Fire Chi","tg_group":"Statewide Fire","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"0bf50598-7592-4e66-8a5b-df2c7fc48445","system_id":197,"tgid":1041,"name":"Southern Rhode Island Fire Chiefs","alpha_tag":"SRI Fire Chi","tg_group":"Statewide Fire","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"92d2572b-cd06-48d2-96c5-2f5054e2d083","system_id":197,"tgid":1314,"name":"Tanker Taskforce 1","alpha_tag":"Tanker TF 1","tg_group":"Statewide Fire","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"6bf84c71-74cb-4476-b64c-c3dbd968b0f7","system_id":197,"tgid":194,"name":"Lifepact Ambulance (Statewide)","alpha_tag":"Lifepact Amb","tg_group":"Statewide EMS and Hospitals","frequency":null,"metadata":null,"tags":["EMS Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"172ed857-94bb-458b-8c3b-525da1786f9f","system_id":197,"tgid":212,"name":"Fatima St Josephs","alpha_tag":"Fatima-St Joes","tg_group":"Statewide EMS and Hospitals","frequency":null,"metadata":null,"tags":["Business"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"ff094279-db19-44eb-97a1-9d0f7bbacbe0","system_id":197,"tgid":220,"name":"Health 1","alpha_tag":"Health 1","tg_group":"Statewide EMS and Hospitals","frequency":null,"metadata":{"encrypted":true},"tags":["EMS-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"255aa5b7-d44b-4c40-b84c-892b9bffd436","system_id":197,"tgid":221,"name":"Health 2","alpha_tag":"Health 2","tg_group":"Statewide EMS and Hospitals","frequency":null,"metadata":{"encrypted":true},"tags":["EMS-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"29b0223b-eea5-44f7-8391-f445d15afd42","system_id":197,"tgid":222,"name":"Department of Health - Statewide","alpha_tag":"Dept of HealthSW","tg_group":"Statewide EMS and Hospitals","frequency":null,"metadata":null,"tags":["EMS-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"94040374-f692-4b98-8bf8-713f8d962d7c","system_id":197,"tgid":228,"name":"DMAT South","alpha_tag":"DMAT South","tg_group":"Statewide EMS and Hospitals","frequency":null,"metadata":{"encrypted":true},"tags":["Emergency Ops"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"8d019192-c242-44e2-8afc-cae3a61fb586","system_id":197,"tgid":232,"name":"Life Span Net 1","alpha_tag":"Life Span 1","tg_group":"Statewide EMS and Hospitals","frequency":null,"metadata":null,"tags":["EMS-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"b14323a6-bc8f-4e7d-b1d9-29333ff99393","system_id":197,"tgid":234,"name":"RI Hospital Operations","alpha_tag":"RI Hosp Ops","tg_group":"Statewide EMS and Hospitals","frequency":null,"metadata":null,"tags":["Business"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"3bea6f5b-3af6-4e03-b436-6c4719e43a1b","system_id":197,"tgid":120,"name":"Law Enforcement Operations","alpha_tag":"DEM PD Ops","tg_group":"Department of Environmental Management","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"067d89bc-7f01-41f5-b398-1659a44ff17a","system_id":197,"tgid":122,"name":"Law Enforcement Police","alpha_tag":"DEM Police","tg_group":"Department of Environmental Management","frequency":null,"metadata":null,"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"4c7215a3-b539-4b1e-9849-c6077dbb5722","system_id":197,"tgid":10,"name":"Emergency Management Agency 1","alpha_tag":"EMA-1","tg_group":"Emergency Management Agency","frequency":null,"metadata":null,"tags":["Emergency Ops"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"f5717a28-9a26-4f97-a479-81998ebea89c","system_id":197,"tgid":20,"name":"Emergency Management Agency","alpha_tag":"EMA","tg_group":"Emergency Management Agency","frequency":null,"metadata":null,"tags":["Emergency Ops"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"0b4b3739-7011-4e82-ad6f-4125c8fa7311","system_id":197,"tgid":4,"name":"Wide Area 3","alpha_tag":"Wide Area 3","tg_group":"Statewide Area/Events","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"e4d7defa-922d-4ae7-b866-67f7e936cd4f","system_id":197,"tgid":5,"name":"Wide Area 4","alpha_tag":"Wide Area 4","tg_group":"Statewide Area/Events","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"24abf7df-866b-4a56-8383-67ad6145de1e","system_id":197,"tgid":6,"name":"Wide Area 5","alpha_tag":"Wide Area 5","tg_group":"Statewide Area/Events","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"e8f4a8b0-993e-4df8-883a-0ad8be9c3978","system_id":197,"tgid":7,"name":"Wide Area 6","alpha_tag":"Wide Area 6","tg_group":"Statewide Area/Events","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"b04883e5-6a15-4a8d-a563-afa467d49dec","system_id":197,"tgid":1018,"name":"Southwide CH-1","alpha_tag":"SOUTHWIDE 1","tg_group":"Statewide Area/Events","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"6a40e9a1-d007-4033-8282-3061bdd0eaa5","system_id":197,"tgid":1019,"name":"Southwide CH-2","alpha_tag":"SOUTHWIDE 2","tg_group":"Statewide Area/Events","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"9f8e4da6-4301-4522-8d0b-29688b734b8e","system_id":197,"tgid":1022,"name":"Wide Area 7","alpha_tag":"WIDE AREA 7","tg_group":"Statewide Area/Events","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"a0f3ca99-36e8-461f-90d7-7c96ea80a7a6","system_id":197,"tgid":1023,"name":"Wide Area 8","alpha_tag":"WIDE AREA 8","tg_group":"Statewide Area/Events","frequency":null,"metadata":{"encrypted":true},"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"65f606f6-a63b-4f3d-bd25-67c18979e4d6","system_id":197,"tgid":1025,"name":"Inland Marine Interop","alpha_tag":"Inland Marine IO","tg_group":"Statewide Area/Events","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"0f26686d-9bf2-4b26-8901-ff354cde1607","system_id":197,"tgid":1037,"name":"Southside CH 5","alpha_tag":"SOUTHSIDE 5","tg_group":"Statewide Area/Events","frequency":null,"metadata":{"encrypted":true},"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"ee294b39-f32b-4c78-a2ba-64f84ab43ca0","system_id":197,"tgid":1173,"name":"North Wide 1","alpha_tag":"NORTHWIDE1","tg_group":"Statewide Area/Events","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"c6e6b91c-1fd3-4e89-9043-4179d3af4491","system_id":197,"tgid":1174,"name":"North Wide 2","alpha_tag":"NORTHWIDE2","tg_group":"Statewide Area/Events","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"a369012d-b92d-484f-839d-1734ff571642","system_id":197,"tgid":1177,"name":"North Wide 5","alpha_tag":"NORTHWIDE5","tg_group":"Statewide Area/Events","frequency":null,"metadata":{"encrypted":true},"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"8953bb68-65fc-492b-8c3a-17c9028be991","system_id":197,"tgid":1185,"name":"Metro Wide 1","alpha_tag":"METROWIDE1","tg_group":"Statewide Area/Events","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"4eb7649c-6c93-4780-8979-d1830356f2a5","system_id":197,"tgid":1186,"name":"Metro Wide 2","alpha_tag":"METROWIDE2","tg_group":"Statewide Area/Events","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"4c3deab2-a4b4-475d-a3af-be8fb56987c7","system_id":197,"tgid":1187,"name":"Metro Wide 3","alpha_tag":"METROWIDE3","tg_group":"Statewide Area/Events","frequency":null,"metadata":{"encrypted":true},"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"7f581852-6f18-44be-8233-50eab13935f3","system_id":197,"tgid":1335,"name":"East Wide 1","alpha_tag":"EASTWIDE 1","tg_group":"Statewide Area/Events","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"1d844845-17e9-44ae-b78a-e151c0075592","system_id":197,"tgid":1336,"name":"East Wide 2","alpha_tag":"EASTWIDE 2","tg_group":"Statewide Area/Events","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"5836b707-5885-450c-b0ec-29a3703934bf","system_id":197,"tgid":1337,"name":"East Wide 3","alpha_tag":"EASTWIDE 3","tg_group":"Statewide Area/Events","frequency":null,"metadata":{"encrypted":true},"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"50a28da1-0297-4ded-a77e-758579ea3dfe","system_id":197,"tgid":11186,"name":"Metro Wide 2","alpha_tag":"METROWIDE2","tg_group":"Statewide Area/Events","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"4136abf7-52b3-4827-9d03-e944b3c9db36","system_id":197,"tgid":1033,"name":"Tanker Taskforce ","alpha_tag":"TANK TF","tg_group":"Statewide Emergency Response","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"6b75045f-8efd-49d2-aae5-411947cb553d","system_id":197,"tgid":1034,"name":"Hazmat 1","alpha_tag":"HZT DC1","tg_group":"Statewide Emergency Response","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"7694267a-ef4e-4cea-806b-32d6108bd685","system_id":197,"tgid":1035,"name":"Hazmat 2","alpha_tag":"HZT DC2","tg_group":"Statewide Emergency Response","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"84f57e37-caac-4e33-beaa-3263a3994370","system_id":197,"tgid":176,"name":"Department of Transportation - Primary","alpha_tag":"RIDOT Primary","tg_group":"Department of Transportation","frequency":null,"metadata":null,"tags":["Public Works"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"24ba9c9b-1467-4a27-8f01-a910ae295f6e","system_id":197,"tgid":4421,"name":"Newport Pell Bridge Operations","alpha_tag":"RITBA - Pell Bdg","tg_group":"Tunnel and Bridge Authority","frequency":null,"metadata":null,"tags":["Public Works"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"fbfe5f5a-bf44-4cde-a63b-5606633e2bf0","system_id":197,"tgid":274,"name":"Providence VA Police","alpha_tag":"VA Police","tg_group":"Federal","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"006f2829-5d7d-4906-9f01-a239c4365854","system_id":197,"tgid":186,"name":"Rhode Island Public Transit Auth.","alpha_tag":"RIPTA","tg_group":"RIPTA","frequency":null,"metadata":{"encrypted":true},"tags":["Transportation"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"c3af7f6b-41d6-41f9-ab9a-8d12f4125732","system_id":197,"tgid":187,"name":"Rhode Island Public Transit Auth.","alpha_tag":"RIPTA","tg_group":"RIPTA","frequency":null,"metadata":null,"tags":["Transportation"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"5fff332f-7576-4062-8556-304a3e3eae14","system_id":197,"tgid":188,"name":"Rhode Island Public Transit Auth.","alpha_tag":"RIPTA","tg_group":"RIPTA","frequency":null,"metadata":null,"tags":["Transportation"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"c28d0cea-39d2-401a-9272-0da85ca1e4b3","system_id":197,"tgid":189,"name":"Rhode Island Public Transit Auth.","alpha_tag":"RIPTA","tg_group":"RIPTA","frequency":null,"metadata":null,"tags":["Transportation"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"8eaf3f44-c6c6-4f83-a2f2-f54fc00e09d6","system_id":197,"tgid":190,"name":"Rhode Island Public Transit. Auth.","alpha_tag":"RIPTA","tg_group":"RIPTA","frequency":null,"metadata":null,"tags":["Transportation"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"fc256408-54c1-4dfc-acaa-8a2cecce5a3a","system_id":197,"tgid":304,"name":"Fire Operations","alpha_tag":"Quonset ANGB FD","tg_group":"Quonset ANGB","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"ba53ab70-5b18-4b94-b4d3-38a5143e6340","system_id":197,"tgid":17,"name":"Airport Police Operations","alpha_tag":"TF Green PD","tg_group":"Rhode Island Airport Commission","frequency":null,"metadata":{"encrypted":true},"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"8d8724b0-cf3f-4e17-a3f7-9be1072fb63c","system_id":197,"tgid":19,"name":"Airport Fire Operations","alpha_tag":"TF Green FD","tg_group":"Rhode Island Airport Commission","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"35d6042c-4160-438e-a9e2-a9f3fb4ffb00","system_id":197,"tgid":1126,"name":"University of Rhode Island Police - Dispatch","alpha_tag":"URI PD","tg_group":"College/Education Security","frequency":null,"metadata":{"encrypted":true},"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"19b454d5-22b5-4fa1-b604-193fb8966710","system_id":197,"tgid":1131,"name":"University of Rhode Island - EMS","alpha_tag":"URI EMS","tg_group":"College/Education Security","frequency":null,"metadata":{"encrypted":true},"tags":["EMS Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"a7960732-ca52-4f53-83f5-20c889b79bf5","system_id":197,"tgid":1348,"name":"St. George's School (Middletown) - Security","alpha_tag":"St George Sec","tg_group":"College/Education Security","frequency":null,"metadata":null,"tags":["Security"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"04cfb57c-7601-432d-989b-accea9d6e263","system_id":197,"tgid":10228,"name":"Rhode Island School of Design - Security","alpha_tag":"RISD Secuty","tg_group":"College/Education Security","frequency":null,"metadata":{"encrypted":true},"tags":["Security"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"e25c2774-1d3f-4c62-8bbb-15d9afbcbf7f","system_id":197,"tgid":10229,"name":"Providence College Security - Dispatch","alpha_tag":"PROV COLL","tg_group":"College/Education Security","frequency":null,"metadata":{"encrypted":true},"tags":["Security"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"7da41ab0-408e-4969-82e2-cdcf233438bf","system_id":197,"tgid":10230,"name":"Rhode Island College Security","alpha_tag":"RI COL SEC","tg_group":"College/Education Security","frequency":null,"metadata":null,"tags":["Security"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"1774ace7-709a-4f09-9e9a-83fdeae0ec55","system_id":197,"tgid":11001,"name":"Brown University Police - Dispatch","alpha_tag":"BROWN UNIV","tg_group":"College/Education Security","frequency":null,"metadata":{"encrypted":true},"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"eb233a9b-5394-4b3c-b856-b546d313c8a3","system_id":197,"tgid":11002,"name":"Brown University Police - Car-to-Car","alpha_tag":"BROWN CAR","tg_group":"College/Education Security","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"b4c1c0e0-5447-44ba-b70e-b36dbcfdec90","system_id":197,"tgid":11003,"name":"Brown University Police - Tactical","alpha_tag":"BROWN TAC","tg_group":"College/Education Security","frequency":null,"metadata":{"encrypted":true},"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"b302dcdc-3b9e-4522-a2a6-f1ed0afec1f8","system_id":197,"tgid":12,"name":"Metro Wide 2","alpha_tag":"METROWIDE2","tg_group":"Statewide Misc.","frequency":null,"metadata":{"encrypted":true},"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"e20faabe-df6b-462e-b17d-3a748a58677a","system_id":197,"tgid":14,"name":"Metro Wide 4","alpha_tag":"METROWIDE4","tg_group":"Statewide Misc.","frequency":null,"metadata":{"encrypted":true},"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"0c56348f-8921-4266-b11d-0f334c62fe52","system_id":197,"tgid":70,"name":"RI Traffic Tribunal Security","alpha_tag":"TFC TRIBUNAL","tg_group":"Statewide Misc.","frequency":null,"metadata":{"encrypted":true},"tags":["Security"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"ba53af19-779c-4294-8b65-70ffa0b77396","system_id":197,"tgid":168,"name":"Rhode Island Red Cross - Primary","alpha_tag":"Red Cross 1","tg_group":"Statewide Misc.","frequency":null,"metadata":null,"tags":["Other"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"3c130ad7-97dd-4afe-8e3a-d29b5125210f","system_id":197,"tgid":169,"name":"Rhode Island Red Cross - Secondary","alpha_tag":"Red Cross 2","tg_group":"Statewide Misc.","frequency":null,"metadata":null,"tags":["Other"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"0ef1c314-090f-47c7-9a6f-571c246f3e9a","system_id":197,"tgid":223,"name":"Statewide Nursing Homes Net","alpha_tag":"NURSING HM","tg_group":"Statewide Misc.","frequency":null,"metadata":null,"tags":["Other"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"c0b7413e-f110-4d58-b00c-e73bff706f7f","system_id":197,"tgid":243,"name":"Hospital Operations","alpha_tag":"Slater Hosp Ops","tg_group":"Statewide Misc.","frequency":null,"metadata":null,"tags":["Business"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"f4b6f440-90a3-4711-b320-8e4e4b89cb51","system_id":197,"tgid":244,"name":"Slater Hospital Security","alpha_tag":"Slater Hosp Sec","tg_group":"Statewide Misc.","frequency":null,"metadata":null,"tags":["Security"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"65ce6400-2cbd-4c28-87aa-113df2468928","system_id":197,"tgid":1042,"name":"County Fireground","alpha_tag":"WashCo FireG","tg_group":"Washington County","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"d5a23b9c-a740-480c-9382-d9c6034ad296","system_id":197,"tgid":1479,"name":"County Fire Station/Station","alpha_tag":"WashCo FireS","tg_group":"Washington County","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"0c796503-e1ce-4217-a5f5-0caf1fbfe831","system_id":197,"tgid":1712,"name":"Fire 1 Dispatch","alpha_tag":"BarringtnFD1","tg_group":"Barrington","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"b10b7bf5-b15c-47a5-bdbf-8e7dcafc9e13","system_id":197,"tgid":1713,"name":"Fire 2","alpha_tag":"BarringtnFD2","tg_group":"Barrington","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"8647a4b4-4ed4-4ce9-a4ed-47f74aa59446","system_id":197,"tgid":1715,"name":"Police Operations","alpha_tag":"BarringtonPD 1","tg_group":"Barrington","frequency":null,"metadata":{"encrypted":true},"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"8ced323c-b76f-4d3f-ac47-6c9fb03fc922","system_id":197,"tgid":1716,"name":"Police Secondary","alpha_tag":"BarringtonPD 2","tg_group":"Barrington","frequency":null,"metadata":null,"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"8fbae88f-d580-463a-8454-b68312207f0a","system_id":197,"tgid":1744,"name":"Fire Operations (Patch from VHF)","alpha_tag":"Bristol FD","tg_group":"Bristol","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"3b584c62-3164-42b4-9753-b5d5027ce15a","system_id":197,"tgid":1755,"name":"Harbormaster","alpha_tag":"Bristol Harbor","tg_group":"Bristol","frequency":null,"metadata":null,"tags":["Public Works"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"4f0a5825-0d8f-450e-b7f2-bf4f0152e5d4","system_id":197,"tgid":2003,"name":"Police","alpha_tag":"Burrville PD","tg_group":"Burrillville","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"9435807f-9d4b-47be-afb7-7970466a5626","system_id":197,"tgid":2004,"name":"Police 2","alpha_tag":"Burrvl PD2","tg_group":"Burrillville","frequency":null,"metadata":null,"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"fe33408c-f9e8-4e2c-b974-08a32d29416b","system_id":197,"tgid":2005,"name":"Police 3 Detectives","alpha_tag":"Burrvl PD3","tg_group":"Burrillville","frequency":null,"metadata":{"encrypted":true},"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"af206a32-9cff-4d4a-b5e4-98320982c85a","system_id":197,"tgid":2006,"name":"Police 4","alpha_tag":"Burrvl PD4","tg_group":"Burrillville","frequency":null,"metadata":null,"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"ad703848-59c0-4a4b-93a1-d5b2f5bfef5a","system_id":197,"tgid":2000,"name":"Fire Misc (Ops are VHF)","alpha_tag":"Burrvl FD","tg_group":"Burrillville","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"6ed92da4-82ca-4956-8e5b-6fe9d8a9ddd9","system_id":197,"tgid":2001,"name":"Fire TAC-1","alpha_tag":"Burvl FDTAC1","tg_group":"Burrillville","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"eb09277b-92ce-4904-aefa-18500944cbe8","system_id":197,"tgid":2009,"name":"Fire TAC-2","alpha_tag":"Burvl FDTAC2","tg_group":"Burrillville","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"00a0b152-7ea6-4729-a861-d2f6497a3235","system_id":197,"tgid":2002,"name":"EMS Misc (Ops are VHF)","alpha_tag":"Burrvl EMS","tg_group":"Burrillville","frequency":null,"metadata":null,"tags":["EMS-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"c37f4192-779e-41d9-ab3b-1c5424fce0b7","system_id":197,"tgid":2007,"name":"Town-Wide","alpha_tag":"Burrvl Town","tg_group":"Burrillville","frequency":null,"metadata":null,"tags":["Multi-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"27b03072-e641-4a76-9f03-abaa40abc944","system_id":197,"tgid":2008,"name":"Emergency Management","alpha_tag":"Burrvl EMA","tg_group":"Burrillville","frequency":null,"metadata":null,"tags":["Emergency Ops"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"8fddeb21-91d9-45c0-8767-af847afd0edb","system_id":197,"tgid":1838,"name":"Police 1 Dispatch","alpha_tag":"CentFallsPD1","tg_group":"Central Falls","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"5d8857b7-99ac-418e-8aff-abe3037ffe7f","system_id":197,"tgid":1839,"name":"Police 2","alpha_tag":"CentFallsPD2","tg_group":"Central Falls","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"a68aa8af-5e39-4c41-ae73-4d373c5ebebc","system_id":197,"tgid":1835,"name":"Fire Dispatch (Simulcast of UHF)","alpha_tag":"CentFalls FD 1","tg_group":"Central Falls","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"9cdcc595-bcce-4c7b-93d8-df93fab7e125","system_id":197,"tgid":1836,"name":"Fireground","alpha_tag":"CentFalls FD 2","tg_group":"Central Falls","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"ddebafe6-5a31-4d5d-81e2-d2ce9c2b1789","system_id":197,"tgid":1425,"name":"Police Operations - Simulcast of UHF","alpha_tag":"CharlestownPD","tg_group":"Charlestown","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"2f0fea19-31a2-4022-8777-a93143dfdcbf","system_id":197,"tgid":1429,"name":"EMS - Linked to 151.3325","alpha_tag":"Chastown EMS","tg_group":"Charlestown","frequency":null,"metadata":null,"tags":["EMS Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"a68406e8-7707-4ff0-8834-e197a4034aa4","system_id":197,"tgid":1483,"name":"Police 1 - Dispatch","alpha_tag":"Coventry PD","tg_group":"Coventry","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"8afa3f85-b8a6-4708-8aeb-bac880b5b89b","system_id":197,"tgid":1484,"name":"Police 2","alpha_tag":"Coventry PD2","tg_group":"Coventry","frequency":null,"metadata":null,"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"93da5381-0164-4021-84e6-48b6226a1b78","system_id":197,"tgid":1480,"name":"Fire","alpha_tag":"Coventry FD","tg_group":"Coventry","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"021851f5-d9ac-4f31-ba89-ddfc454c5f8f","system_id":197,"tgid":1500,"name":"Fire - Dispatch/Operations","alpha_tag":"Cranston FD Disp","tg_group":"Cranston","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"72ac89b3-8b19-4537-84c1-9e9beac03c87","system_id":197,"tgid":1501,"name":"Fire - Fireground 2","alpha_tag":"Cranston FD FG2","tg_group":"Cranston","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"5a27db02-9de3-4ae3-ba42-318813487685","system_id":197,"tgid":1502,"name":"Fire - Fireground 3","alpha_tag":"Cranston FD FG3","tg_group":"Cranston","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"929359ca-8c5e-494e-952d-c1af42ea3d16","system_id":197,"tgid":1503,"name":"Fire - Fireground 4","alpha_tag":"Cranston FD FG4","tg_group":"Cranston","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"76c1bdd1-9ab8-4292-9c6d-aee4de5ef9f9","system_id":197,"tgid":1504,"name":"Fire - Admin/Alt Fireground 5","alpha_tag":"Cranston FD Admi","tg_group":"Cranston","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"dcf08dfc-bd02-4808-8939-8585928a0f7d","system_id":197,"tgid":1520,"name":"Fire","alpha_tag":"Cumberland FD","tg_group":"Cumberland","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"e50be1a6-dc1d-4768-a853-7988fddce562","system_id":197,"tgid":1523,"name":"Police Secondary","alpha_tag":"Cumberland PD","tg_group":"Cumberland","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"e9b948c9-18bb-43e9-b3e5-c400cde5e60c","system_id":197,"tgid":1776,"name":"Fire Talk Around","alpha_tag":"E Greenwich F-TA","tg_group":"East Greenwich","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"5ead6fc7-ae77-4a1d-a59b-188a4b21c86f","system_id":197,"tgid":1779,"name":"Police Operations","alpha_tag":"E Greenwich PD","tg_group":"East Greenwich","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"bc23d728-b453-47ea-9a65-0af24c56d080","system_id":197,"tgid":1869,"name":"Police 1 - Dispatch","alpha_tag":"E Prov PD 1","tg_group":"East Providence","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"0a869133-2088-4805-bd55-c446e25eb075","system_id":197,"tgid":1872,"name":"Police 2","alpha_tag":"E Prov PD 2","tg_group":"East Providence","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"90bafccc-bec6-4775-b640-1d9a2b7f512b","system_id":197,"tgid":1870,"name":"Police 3","alpha_tag":"E Prov PD 3","tg_group":"East Providence","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"54bfc9d0-0532-4df5-aaa7-c3a96bc59b48","system_id":197,"tgid":1883,"name":"Detectives","alpha_tag":"E Prov PD12","tg_group":"East Providence","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"9f77d904-2c5b-4e26-b163-defde5ee6a0f","system_id":197,"tgid":1866,"name":"Fire - Dispatch/Operations","alpha_tag":"E Prov FD 1","tg_group":"East Providence","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"bb3e9346-cef8-4f0a-a951-5ef30fa47a36","system_id":197,"tgid":1867,"name":"Fire \"Channel 2\"","alpha_tag":"E Prov FD 2","tg_group":"East Providence","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"4e75aea9-e111-4596-a685-a591121966e0","system_id":197,"tgid":1878,"name":"Fire \"Channel 3\"","alpha_tag":"E Prov FD 3","tg_group":"East Providence","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"31650d51-0354-4a84-9580-ff560760fd36","system_id":197,"tgid":2064,"name":"Fire - Fireground","alpha_tag":"Exeter FD-G","tg_group":"Exeter","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"514ca197-c875-41d0-ad92-16eba7627e23","system_id":197,"tgid":1904,"name":"Fire","alpha_tag":"Foster Fire","tg_group":"Foster","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"98322eb5-cf43-472b-92e5-b887d4630fb8","system_id":197,"tgid":1939,"name":"Police","alpha_tag":"Glocester PD","tg_group":"Glocester","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"d4747ead-6eb8-4acd-9c5b-078143ee26a5","system_id":197,"tgid":1940,"name":"Police Secondary","alpha_tag":"Glocester PD 2","tg_group":"Glocester","frequency":null,"metadata":null,"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"86ad2313-9d50-4172-b470-bf24a865837c","system_id":197,"tgid":1410,"name":"Police","alpha_tag":"Hopkinton PD","tg_group":"Hopkinton","frequency":null,"metadata":{"encrypted":true},"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"9123461c-41f5-4f99-aa99-ce24eb4d7885","system_id":197,"tgid":1100,"name":"Police 1 - Dispatch","alpha_tag":"Jamestown PD 1","tg_group":"Jamestown","frequency":null,"metadata":{"encrypted":true},"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"76e3336e-6549-4622-958f-df297b9fa007","system_id":197,"tgid":1101,"name":"Police 2","alpha_tag":"Jamestown PD 2","tg_group":"Jamestown","frequency":null,"metadata":{"encrypted":true},"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"864bafd7-cd4c-41b2-bb57-66ab431a032b","system_id":197,"tgid":1108,"name":"Fire","alpha_tag":"Jamestown FD","tg_group":"Jamestown","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"72b9a7e9-37ed-448d-8801-f29055d3090d","system_id":197,"tgid":1120,"name":"Fireground 1","alpha_tag":"Jamestown FG 1","tg_group":"Jamestown","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"24637182-54f9-4424-83c7-b98b938045da","system_id":197,"tgid":1121,"name":"Fireground 2","alpha_tag":"Jamestown FG 2","tg_group":"Jamestown","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"51984385-4b0e-43f7-ba95-1a493f321f09","system_id":197,"tgid":1114,"name":"Public Works","alpha_tag":"Jamestown DPW","tg_group":"Jamestown","frequency":null,"metadata":null,"tags":["Public Works"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"66603022-c1df-4579-b99e-d9d20d573ad5","system_id":197,"tgid":1107,"name":"Town Schools","alpha_tag":"Jamestown School","tg_group":"Jamestown","frequency":null,"metadata":null,"tags":["Schools"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"3171c8fe-f7f1-44e4-a13b-b365b2ebb44f","system_id":197,"tgid":1619,"name":"Police Operations","alpha_tag":"Johnston PD","tg_group":"Johnston","frequency":null,"metadata":{"encrypted":true},"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"0ffb6907-1363-45cd-8838-f0bdd4c812f0","system_id":197,"tgid":1616,"name":"Fire Operations","alpha_tag":"Johnston FD","tg_group":"Johnston","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"42577410-aca0-48c2-afbc-4c79c62572e2","system_id":197,"tgid":1617,"name":"Fireground","alpha_tag":"Johnston FG","tg_group":"Johnston","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"0f8ed94e-e62b-4de7-aa1c-c84c887e1f7c","system_id":197,"tgid":1683,"name":"Police F1","alpha_tag":"Lincoln Police","tg_group":"Lincoln","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"31e927df-e52a-4f8f-8662-7eb5d3a4fe16","system_id":197,"tgid":1684,"name":"Police F2","alpha_tag":"Lincoln Police 2","tg_group":"Lincoln","frequency":null,"metadata":null,"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"fafce236-23e1-46c9-9fff-7fbaff4ffe94","system_id":197,"tgid":1680,"name":"Fire Dispatch","alpha_tag":"Lincoln Fire 1","tg_group":"Lincoln","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"f4589733-e563-419d-b045-aad3e226488a","system_id":197,"tgid":1681,"name":"Fireground 2","alpha_tag":"Lincoln Fire 2","tg_group":"Lincoln","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"c02cca42-91ae-4169-9ce5-039d6ab00e40","system_id":197,"tgid":1691,"name":"Fireground 3","alpha_tag":"Lincoln Fire 3","tg_group":"Lincoln","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"f67aab29-332d-4144-8b35-507c7c8a09c4","system_id":197,"tgid":1682,"name":"EMS","alpha_tag":"Lincoln EMS","tg_group":"Lincoln","frequency":null,"metadata":null,"tags":["EMS Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"db07105d-c310-4362-8405-da3b2169f5a9","system_id":197,"tgid":1688,"name":"Emergency Management","alpha_tag":"Lincoln EMA","tg_group":"Lincoln","frequency":null,"metadata":null,"tags":["Emergency Ops"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"10c9d009-6e5e-4ef1-b570-680746acd0cc","system_id":197,"tgid":1687,"name":"Townwide","alpha_tag":"Lincoln Townwide","tg_group":"Lincoln","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"7760331b-6631-48d6-9342-b051b5df4106","system_id":197,"tgid":1692,"name":"Public Works","alpha_tag":"Lincoln DPW","tg_group":"Lincoln","frequency":null,"metadata":null,"tags":["Public Works"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"37cf7aee-9b0c-4c10-a8f9-980630f34ce0","system_id":197,"tgid":1264,"name":"Police","alpha_tag":"LittleCompPD","tg_group":"Little Compton","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"01c0ab7a-c65e-402d-b9b2-16cbc50e73a3","system_id":197,"tgid":1266,"name":"Fire","alpha_tag":"LittleCompFD","tg_group":"Little Compton","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"2eaf9364-01e2-406b-98b8-2c30d346bc4b","system_id":197,"tgid":1338,"name":"Police Operations","alpha_tag":"MiddletownPD","tg_group":"Middletown","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"2fa319f2-45a8-457e-8122-eaf4ad5425c2","system_id":197,"tgid":1343,"name":"Fire Operations","alpha_tag":"Middletown FD","tg_group":"Middletown","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"49ee160e-17b9-4541-82ae-e5df820ac85d","system_id":197,"tgid":1345,"name":"Townwide","alpha_tag":"MiddletownTW","tg_group":"Middletown","frequency":null,"metadata":null,"tags":["Multi-Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"e3f8e784-870f-487a-b6cc-0d163833df63","system_id":197,"tgid":1001,"name":"Police - Dispatch","alpha_tag":"Narrag PD 1","tg_group":"Narragansett","frequency":null,"metadata":{"encrypted":true},"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"6613a9cc-9474-47b6-9928-35b9f6f4f8c0","system_id":197,"tgid":1002,"name":"Police - Car/Car","alpha_tag":"Narrag PD 2","tg_group":"Narragansett","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"e70dbeeb-ae7b-44cd-b9bc-41033aa5baf4","system_id":197,"tgid":1003,"name":"Police - Special Details 1/Town Beaches","alpha_tag":"Narrag PD 3","tg_group":"Narragansett","frequency":null,"metadata":{"encrypted":true},"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"0d45e24d-72ea-44a2-8e3c-a030c9937ab8","system_id":197,"tgid":1004,"name":"Police - Special Details 2","alpha_tag":"Narrag PD 4","tg_group":"Narragansett","frequency":null,"metadata":{"encrypted":true},"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"409a7cbf-05ae-41f9-b425-254543d94d11","system_id":197,"tgid":1005,"name":"Police - Harbormaster","alpha_tag":"Narrag PD 5","tg_group":"Narragansett","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"5900b90a-e703-497d-9856-d2441d14ba49","system_id":197,"tgid":1007,"name":"Police - Detectives","alpha_tag":"Narrag PD 7","tg_group":"Narragansett","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"a677de8b-18cb-454b-99dd-d9daa7ccbb75","system_id":197,"tgid":1008,"name":"Police - Detectives","alpha_tag":"Narrag PD 8","tg_group":"Narragansett","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"00dae4e2-e5df-4cf3-859e-bddada6745fb","system_id":197,"tgid":1006,"name":"Fire - Dispatch","alpha_tag":"Narrag FD","tg_group":"Narragansett","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"a6a04c5c-37c7-4a35-836f-11732ce8bc27","system_id":197,"tgid":1012,"name":"Fire - Fireground 1","alpha_tag":"Narrag FDFG1","tg_group":"Narragansett","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"b4886861-1fc7-4c82-a491-bfabd7a19df5","system_id":197,"tgid":1013,"name":"Fire - Fireground 2","alpha_tag":"Narrag FDFG2","tg_group":"Narragansett","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"0fdc78a5-5dbb-42fd-b7f9-296566557fab","system_id":197,"tgid":1016,"name":"Fire - Administration","alpha_tag":"Narrag FD AD","tg_group":"Narragansett","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"885b039f-30e7-46f0-8d59-61e19b642221","system_id":197,"tgid":1014,"name":"Fire - EMS Ops","alpha_tag":"Narrag EMS","tg_group":"Narragansett","frequency":null,"metadata":null,"tags":["EMS Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"db44a694-97b8-4d99-808f-e1e037c68bf7","system_id":197,"tgid":1017,"name":"Public Works","alpha_tag":"Narrag DPW","tg_group":"Narragansett","frequency":null,"metadata":null,"tags":["Public Works"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"c5e5de1d-2c68-4923-88ec-1189fb2e3697","system_id":197,"tgid":1010,"name":"Town Administration","alpha_tag":"Narrag TownA","tg_group":"Narragansett","frequency":null,"metadata":null,"tags":["Other"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"3cef09ff-14be-4392-a801-f6eaee414091","system_id":197,"tgid":1011,"name":"Townwide Interop","alpha_tag":"Narrag IOP","tg_group":"Narragansett","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"58b45f2d-ec82-417c-aaba-160cd640ff73","system_id":197,"tgid":1376,"name":"Police","alpha_tag":"New Shore PD","tg_group":"New Shoreham","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"495fe4a0-5ce1-402c-a728-7ed3235b95e6","system_id":197,"tgid":1300,"name":"Police 1 - Dispatch","alpha_tag":"Newport PD 1","tg_group":"Newport","frequency":null,"metadata":{"encrypted":true},"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"9f571fa5-e656-4aa5-9fae-1ebdd7aa6269","system_id":197,"tgid":1302,"name":"Police 2 - Records","alpha_tag":"Newport PD 2","tg_group":"Newport","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"c2ec7f40-57b3-4593-bc84-888c970fd528","system_id":197,"tgid":1304,"name":"Police 4 - Tactical 1","alpha_tag":"Newport PD 4","tg_group":"Newport","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"d4a99a1e-ab9d-4420-9345-37cd6d02282e","system_id":197,"tgid":1307,"name":"Police 7 - Tactical 4","alpha_tag":"Newport PD 7","tg_group":"Newport","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"0981e140-232a-4a87-b83a-21d1845c408a","system_id":197,"tgid":1308,"name":"Police 8 - Tactical 5","alpha_tag":"Newport PD 8","tg_group":"Newport","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"d7570438-1303-4a0b-95a3-0dcca6e3aa2d","system_id":197,"tgid":1303,"name":"Fire Dispatch/Operations","alpha_tag":"Newport FD1","tg_group":"Newport","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"f04715d8-7927-4a96-879a-4f3690ac2025","system_id":197,"tgid":1305,"name":"Fireground Ops 1","alpha_tag":"Newport FG1","tg_group":"Newport","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"a60c7db1-5e05-41eb-834b-734355fe4a05","system_id":197,"tgid":1306,"name":"Fireground Ops 2","alpha_tag":"Newport FG2","tg_group":"Newport","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"9bd3899d-920e-45f1-846d-432f9b08e64d","system_id":197,"tgid":1301,"name":"Fire - Training","alpha_tag":"Newport FDT","tg_group":"Newport","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"7f9b3896-5d5a-47a7-ac18-3c3833e1a342","system_id":197,"tgid":1291,"name":"Water Department","alpha_tag":"Newport Water","tg_group":"Newport","frequency":null,"metadata":null,"tags":["Public Works"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"5ead69d4-f975-412f-91a4-9ed832f69e6e","system_id":197,"tgid":1293,"name":"Public Works","alpha_tag":"Newport DPW","tg_group":"Newport","frequency":null,"metadata":null,"tags":["Public Works"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"9c63b453-ec04-4c9e-ba5c-f944232d1035","system_id":197,"tgid":1297,"name":"Citywide Events","alpha_tag":"Newport Evnt","tg_group":"Newport","frequency":null,"metadata":null,"tags":["Public Works"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"3f64434a-bae0-40f6-906a-d3fdb1f4415b","system_id":197,"tgid":1312,"name":"Newport Citywide","alpha_tag":"Newport CW","tg_group":"Newport","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"0af9ce8c-208b-420e-a526-741539fa3203","system_id":197,"tgid":1285,"name":"Police 1 - Dispatch","alpha_tag":"NKing PD 1","tg_group":"North Kingstown","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"c77ecba4-10fd-4718-b227-e0b430f9bcb0","system_id":197,"tgid":1286,"name":"Police 2 - Admin","alpha_tag":"NKing PD 2","tg_group":"North Kingstown","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"49a3d385-40dc-4229-a912-0ce80f2007cd","system_id":197,"tgid":1287,"name":"Police 3 - Car/Car","alpha_tag":"NKing PD 3","tg_group":"North Kingstown","frequency":null,"metadata":null,"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"42a708a7-21aa-4998-bb45-d4e428811984","system_id":197,"tgid":1280,"name":"Fire - Dispatch","alpha_tag":"NKing Fire D","tg_group":"North Kingstown","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"ecad349c-c35d-4935-95ce-fe0b002cee5e","system_id":197,"tgid":1281,"name":"Fire - Fireground","alpha_tag":"NKing Fire G","tg_group":"North Kingstown","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"71c47935-e281-4bfc-8b8b-652b69ccb092","system_id":197,"tgid":1536,"name":"Police 1 - Dispatch","alpha_tag":"NorthPrv PD1","tg_group":"North Providence","frequency":null,"metadata":{"encrypted":true},"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"e55a20f1-b9f9-4d04-a296-124621928739","system_id":197,"tgid":1537,"name":"Police 2 - Car/Car","alpha_tag":"NorthPrv PD2","tg_group":"North Providence","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"a86671cc-1801-42b9-93e3-bf9d19f825c3","system_id":197,"tgid":1538,"name":"Police 3 - Tactical","alpha_tag":"NorthPrv PD3","tg_group":"North Providence","frequency":null,"metadata":{"encrypted":true},"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"dd54ae16-88e4-4efb-9efe-65dcdad34bc8","system_id":197,"tgid":1547,"name":"Fire Dispatch ","alpha_tag":"NorthPrv FDD","tg_group":"North Providence","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"60010e7c-8c99-4cd5-b9e3-20ca7d39d4ba","system_id":197,"tgid":1548,"name":"Fire 2","alpha_tag":"NorthPrv Fire 2","tg_group":"North Providence","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"801a175b-1c76-4057-832f-3f36d7d893e2","system_id":197,"tgid":1549,"name":"Fire 3","alpha_tag":"NorthPrv Fire 3","tg_group":"North Providence","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"16e4c7bb-db54-4d0b-a484-49330027368b","system_id":197,"tgid":1550,"name":"Fire 4","alpha_tag":"NorthPrv Fire 4","tg_group":"North Providence","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"34f9c697-76b4-4915-b2da-1c5be68ef4ee","system_id":197,"tgid":1551,"name":"Fire 5","alpha_tag":"NorthPrv Fire 5","tg_group":"North Providence","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"be8cb8fa-7dc5-483f-b70c-2c896334cb1f","system_id":197,"tgid":1552,"name":"Fire 6","alpha_tag":"NorthPrv Fire 6","tg_group":"North Providence","frequency":null,"metadata":{"encrypted":true},"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"9cb5dfe0-44fa-4861-97ff-5dfd02f2ba38","system_id":197,"tgid":1544,"name":"Townwide 1","alpha_tag":"NorthPrv TownW 1","tg_group":"North Providence","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"84c53dd7-18c8-460d-a743-a8e9d4aeae20","system_id":197,"tgid":1545,"name":"Townwide 2","alpha_tag":"NorthPrv TownW 2","tg_group":"North Providence","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"ccef002d-82ca-4525-92b8-d8f2a8df3b0c","system_id":197,"tgid":1554,"name":"Public Works","alpha_tag":"NorthPrv DPW","tg_group":"North Providence","frequency":null,"metadata":null,"tags":["Public Works"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"35f15b9b-370d-4a80-94ca-8e9a133eb520","system_id":197,"tgid":1971,"name":"Police","alpha_tag":"N Smithfd PD","tg_group":"North Smithfield","frequency":null,"metadata":{"encrypted":true},"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"94f2dd5c-0873-4f52-b15d-828846e37df6","system_id":197,"tgid":1968,"name":"Fire Dispatch/Operations","alpha_tag":"N Smithfield FD","tg_group":"North Smithfield","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"8fd10658-b480-42ac-8423-3633957e688e","system_id":197,"tgid":1969,"name":"Fire Secondary","alpha_tag":"N Smithfield FD2","tg_group":"North Smithfield","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"924ffe37-13b5-4c76-bd8a-56da8bb07daa","system_id":197,"tgid":1981,"name":"Fireground","alpha_tag":"N Smithfield FD3","tg_group":"North Smithfield","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"8eb4eb8f-7334-4992-96e2-766a4109150e","system_id":197,"tgid":1440,"name":"Fire - Operations","alpha_tag":"Pawtucket FD 1","tg_group":"Pawtucket","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"ed424f0f-7435-43cd-aa66-e5baaa03edc9","system_id":197,"tgid":1441,"name":"Fireground","alpha_tag":"Pawtucket FG","tg_group":"Pawtucket","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"18e8305b-b19f-40c6-b4dd-b4aa3886cb50","system_id":197,"tgid":1442,"name":"EMS Tac","alpha_tag":"Pawtucket EMSTac","tg_group":"Pawtucket","frequency":null,"metadata":null,"tags":["EMS-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"90940fc6-d4ca-4e21-9380-9e4ed60a0e2a","system_id":197,"tgid":1248,"name":"Police","alpha_tag":"PortsmouthPD","tg_group":"Portsmouth","frequency":null,"metadata":{"encrypted":true},"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"f07f1b2a-6bb5-4601-ba57-8a27cbdc20a1","system_id":197,"tgid":1253,"name":"Fire Dispatch (Patch to VHF Primary)","alpha_tag":"Portsmouth FD","tg_group":"Portsmouth","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"759f76b0-889a-43ce-a5ce-3ca91a4eb5c2","system_id":197,"tgid":1255,"name":"Fireground","alpha_tag":"Portsmouth FG","tg_group":"Portsmouth","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"f8580819-da04-402c-8177-0c01746de44f","system_id":197,"tgid":1262,"name":"Island Fire Dispatch","alpha_tag":"Prudence Isl FD","tg_group":"Portsmouth","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"3db6e340-2e78-43db-b635-516e87b33e4b","system_id":197,"tgid":10000,"name":"Police - All Call - Emergency Broadcasts","alpha_tag":"PPD ATG","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Emergency Ops"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"412ba3df-6854-4920-b5ea-27ec09771095","system_id":197,"tgid":10001,"name":"Police 1 - Dispatch","alpha_tag":"PPD CH 1","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"4f42158b-dba6-4d48-94c0-64b411253867","system_id":197,"tgid":10002,"name":"Police 2","alpha_tag":"PPD CH 2","tg_group":"Providence (City)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"6095467c-89ba-48e6-a543-758d7093a494","system_id":197,"tgid":10003,"name":"Police 3","alpha_tag":"PPD CH 3","tg_group":"Providence (City)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"df5cc36d-09c7-4647-aa41-f29c380a987b","system_id":197,"tgid":10004,"name":"Police 4","alpha_tag":"PPD CH-4","tg_group":"Providence (City)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"1ecdcf84-765f-4e5d-bcee-fc1c02181f57","system_id":197,"tgid":10005,"name":"Police 5 -Detectives 1","alpha_tag":"PPD DETEC 1","tg_group":"Providence (City)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"0f44fcd6-29f0-4dc1-af53-c9ae0d8869fe","system_id":197,"tgid":10006,"name":"Police 6 - Car-to-Car","alpha_tag":"PPD T/A","tg_group":"Providence (City)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"67fdc7a2-c67b-425f-93c5-be8d9f630c1d","system_id":197,"tgid":10007,"name":"Police 7 - Narcotics 1","alpha_tag":"PPD NARC 1","tg_group":"Providence (City)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"063c02fd-75cf-44c1-aec9-d2e2ef6e6431","system_id":197,"tgid":10008,"name":"Police 8 - Narcotics 2","alpha_tag":"PPD NARC 2","tg_group":"Providence (City)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"d5f5ad04-8907-4dc6-9f46-494dccf403da","system_id":197,"tgid":10009,"name":"Police 9 - Detectives 2","alpha_tag":"PPD DETEC 2","tg_group":"Providence (City)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"d7f09417-0d2c-4e29-8198-b0f341e284c4","system_id":197,"tgid":10010,"name":"Police 10 - Special Details 1","alpha_tag":"PPD DETAIL 1","tg_group":"Providence (City)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"be8fa60c-1a47-4d6b-955d-d2c04dad86d2","system_id":197,"tgid":10011,"name":"Police 11 - Special Details 2","alpha_tag":"PPD DETAIL 2","tg_group":"Providence (City)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"053d5d25-b014-43d8-b643-22cdcb5004fa","system_id":197,"tgid":10012,"name":"Police 12 - Corrections Security","alpha_tag":"PPD CORR SEC","tg_group":"Providence (City)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"a46cfa2d-6ad2-4f93-bbc3-bd9a5a74660a","system_id":197,"tgid":10013,"name":"Police 13 - Special Response Unit","alpha_tag":"PPD SRU","tg_group":"Providence (City)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"f3d048a9-a436-44c0-a504-27d9a6219197","system_id":197,"tgid":10014,"name":"Police 14 - Administration","alpha_tag":"PPD ADMIN","tg_group":"Providence (City)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"a3f3633f-8417-43ba-bc27-f3619f387b6b","system_id":197,"tgid":10100,"name":"Fire All Call - Emergency Broadcasts","alpha_tag":"PROV FD ATG","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Emergency Ops"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"1a6cb9c1-dc22-4674-aa02-0724d137da2c","system_id":197,"tgid":10101,"name":"Fire Dispatch","alpha_tag":"PFD DISPATCH","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"b87b1615-d512-474f-a474-7dd1e17d02c9","system_id":197,"tgid":10107,"name":"Fireground 2","alpha_tag":"PFD CH-2 FG","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"462a44fe-c150-4a3a-8f99-cc1e4953365e","system_id":197,"tgid":10108,"name":"Fireground 3","alpha_tag":"PFD CH-3 FG","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"4299565e-1085-45b1-b62e-1d4ba18e17a5","system_id":197,"tgid":10109,"name":"Fireground 4","alpha_tag":"PFD CH-4 FG","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"2164418b-fd1a-433f-bfb3-a126c860830a","system_id":197,"tgid":10102,"name":"Fire 5","alpha_tag":"PFD CH-5","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"87293d92-71da-436e-8398-c1e37fb75c4b","system_id":197,"tgid":10103,"name":"Fire 6","alpha_tag":"PFD CH-6","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"f02786e1-faf4-4610-8d13-77fbb9ae1806","system_id":197,"tgid":10104,"name":"Fire 7","alpha_tag":"PFD CH-7","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"55a0abef-bad7-40c0-9473-469f1eca5a66","system_id":197,"tgid":10110,"name":"Fire - Mutual Aid 1","alpha_tag":"PFD M/A 1","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"d53fa3dc-7cd3-47c3-b041-1d7e145f96eb","system_id":197,"tgid":10111,"name":"Fire - Mutual Aid 2","alpha_tag":"PFD M/A 2","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"9654ab94-913d-4a50-ba50-f9e773842f4d","system_id":197,"tgid":10112,"name":"Fire - Mutual Aid 3","alpha_tag":"PFD M/A 3","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"2a5faa60-869b-4365-8305-11f2ededd03e","system_id":197,"tgid":10113,"name":"Fireground 8","alpha_tag":"PFD Fireground 8","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"0a73000e-db60-49a2-9a5f-5e194cf3b566","system_id":197,"tgid":10105,"name":"Fire - Administration","alpha_tag":"PFD ADMIN","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"7a694690-3845-49d1-96f8-d2fd93b2aed5","system_id":197,"tgid":10106,"name":"Fire - Communications","alpha_tag":"PFD COMM","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"5b7d44b5-b054-43f3-8e78-8e4fdf36e591","system_id":197,"tgid":10207,"name":"Public Works","alpha_tag":"PROV DPW","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Public Works"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"568c41d1-052c-4d0f-8b68-ca4c4bf5090d","system_id":197,"tgid":2035,"name":"Police","alpha_tag":"Richmond PD","tg_group":"Richmond","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"57df9db6-f0d9-4dd8-b11b-804f331adb7e","system_id":197,"tgid":2042,"name":"Chariho Regional High School","alpha_tag":"Chariho Reg HS","tg_group":"Richmond","frequency":null,"metadata":null,"tags":["Schools"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"fb087a56-04e9-422b-8d54-db40bcbc6e27","system_id":197,"tgid":1460,"name":"Police","alpha_tag":"Scituate PD","tg_group":"Scituate","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"2ff5eadd-fc14-4145-9e59-f0554c582513","system_id":197,"tgid":1463,"name":"Fire Operations","alpha_tag":"Scituate FD","tg_group":"Scituate","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"42134a8d-aaef-4498-869b-a581ef1da251","system_id":197,"tgid":1651,"name":"Police Operations","alpha_tag":"SmithfieldPD","tg_group":"Smithfield","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"0be92843-487a-4eb8-911c-79a6f0195fc3","system_id":197,"tgid":1652,"name":"Police Secondary","alpha_tag":"Smfld PD 2","tg_group":"Smithfield","frequency":null,"metadata":null,"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"8ad6aee9-3c1d-42b5-897e-aa38ad8f47ab","system_id":197,"tgid":1653,"name":"Police Detectives","alpha_tag":"Smfld PD Det","tg_group":"Smithfield","frequency":null,"metadata":{"encrypted":true},"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"2fe0e3aa-3e6a-4cbf-94c1-6d468433185f","system_id":197,"tgid":1654,"name":"Police Admin","alpha_tag":"Smfld PD Adm","tg_group":"Smithfield","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"c61c861b-96ca-45e3-8d31-f24d6f56ee85","system_id":197,"tgid":1661,"name":"Police Details","alpha_tag":"Smfld PD Dtl","tg_group":"Smithfield","frequency":null,"metadata":null,"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"092314a4-d765-4205-8153-22f1c97613c0","system_id":197,"tgid":1648,"name":"Fire - Fireground","alpha_tag":"SmithfieldFD","tg_group":"Smithfield","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"79eae292-ba96-4e10-91e7-00164e518b24","system_id":197,"tgid":1655,"name":"Town-Wide","alpha_tag":"Smfld Town","tg_group":"Smithfield","frequency":null,"metadata":null,"tags":["Multi-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"3f424c46-f9ea-43db-9c2c-34b512c403c1","system_id":197,"tgid":1657,"name":"Emergency Management","alpha_tag":"Smfld EMA","tg_group":"Smithfield","frequency":null,"metadata":null,"tags":["Emergency Ops"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"28ee1903-0a62-4651-bb80-5a072512a5e4","system_id":197,"tgid":1660,"name":"Public Works","alpha_tag":"Smfld DPW","tg_group":"Smithfield","frequency":null,"metadata":null,"tags":["Public Works"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"cd274b7f-d1fa-43f8-b005-8208ff1a063b","system_id":197,"tgid":1225,"name":"Police 1 - Dispatch","alpha_tag":"SKing PD 1","tg_group":"South Kingstown","frequency":null,"metadata":{"encrypted":true},"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"41039c74-036b-4b3d-a8b1-a0b93135a710","system_id":197,"tgid":1226,"name":"Police 2 - Car/Car","alpha_tag":"SKing PD 2","tg_group":"South Kingstown","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"352da0f6-c312-43a0-9d1f-2329651bb3ab","system_id":197,"tgid":1235,"name":"Police 3 - Tactical","alpha_tag":"SKing PD 3","tg_group":"South Kingstown","frequency":null,"metadata":{"encrypted":true},"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"3984ab59-1f22-47e7-9cd4-4835e7a1a1b6","system_id":197,"tgid":1236,"name":"Police 5 - Tactical","alpha_tag":"SKing PD 5","tg_group":"South Kingstown","frequency":null,"metadata":{"encrypted":true},"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"6d8595f7-aef9-4f39-9141-7d2d31ea3599","system_id":197,"tgid":1232,"name":"Fire - UHF Simulcast","alpha_tag":"SKing FD Lnk","tg_group":"South Kingstown","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"d405ff4b-5999-486f-92f3-259b452909b5","system_id":197,"tgid":1240,"name":"Fire - Detail","alpha_tag":"SKing Fire D","tg_group":"South Kingstown","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"7937d853-64d6-423d-ab4f-14e0d9fcee91","system_id":197,"tgid":1227,"name":"Union Fire District - Fireground 1","alpha_tag":"UnionFD FG 1","tg_group":"South Kingstown","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"84df5994-fdc1-4f04-9c02-5c8d561adb0e","system_id":197,"tgid":1237,"name":"Union Fire District - Fireground 2","alpha_tag":"UnionFD FG 2","tg_group":"South Kingstown","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"7dfd4748-fd4b-40f8-8e53-322471a410cd","system_id":197,"tgid":1026,"name":"Union Fire District - Special Events","alpha_tag":"UnionFD Evnt","tg_group":"South Kingstown","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"b3fd88e4-8b2e-4eb7-ae5d-ae994cb5eae3","system_id":197,"tgid":1015,"name":"EMS","alpha_tag":"SKing EMS","tg_group":"South Kingstown","frequency":null,"metadata":{"encrypted":true},"tags":["EMS Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"eaf21cf9-005d-4560-96d2-2e4d9b97d7e9","system_id":197,"tgid":1316,"name":"Police (Simulcast 482.9625)","alpha_tag":"Tiverton PD","tg_group":"Tiverton","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"e488751a-fcd7-4aa1-b6c0-fcde9316f676","system_id":197,"tgid":1315,"name":"Fire (Simulcast 471.7875)","alpha_tag":"Tiverton FD","tg_group":"Tiverton","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"fd527d9c-4210-4b85-9639-f09ea70533d2","system_id":197,"tgid":1162,"name":"Fire","alpha_tag":"Warwick FD","tg_group":"Warwick","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"6fc60cbe-b4b7-4ed5-94fc-99177620b28c","system_id":197,"tgid":1170,"name":"Fireground","alpha_tag":"Warwick FG","tg_group":"Warwick","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"a6f56a71-6f8c-4384-811c-3e356e7c793a","system_id":197,"tgid":1805,"name":"Police","alpha_tag":"W Greenwh PD","tg_group":"West Greenwich","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"cf114c62-4dc8-4ace-b8e6-7bff2a60e5b2","system_id":197,"tgid":1806,"name":"Police Secondary","alpha_tag":"W GreenwichPD2","tg_group":"West Greenwich","frequency":null,"metadata":null,"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"a6c20723-c1b9-4003-a115-b304c0237924","system_id":197,"tgid":1208,"name":"Fire Operations","alpha_tag":"W Warwick FD","tg_group":"West Warwick","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"48794546-a247-4f04-a94d-7a616215e5dd","system_id":197,"tgid":1050,"name":"Police 1 - Dispatch","alpha_tag":"Westerly PD1","tg_group":"Westerly","frequency":null,"metadata":{"encrypted":true},"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"6c40a65b-b6ed-4508-8368-0b14c176c327","system_id":197,"tgid":1051,"name":"Police 2","alpha_tag":"Westerly PD2","tg_group":"Westerly","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"fdfb1ee2-1962-4000-ab7d-eb4e5de87db2","system_id":197,"tgid":1052,"name":"Police 3","alpha_tag":"Westerly PD3","tg_group":"Westerly","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"1989d13c-3ab0-462d-9d2a-52ef4ca0d366","system_id":197,"tgid":1053,"name":"Police 4","alpha_tag":"Westerly PD4","tg_group":"Westerly","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"ae06a314-f50e-4a21-9924-7f814037798c","system_id":197,"tgid":1054,"name":"Police 5 - Reserve Officers","alpha_tag":"Westerly PD5","tg_group":"Westerly","frequency":null,"metadata":null,"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"c5e10a63-de02-4477-9ecd-eb8a8e0c2792","system_id":197,"tgid":1064,"name":"Police 6 - Traffic Division","alpha_tag":"Westerly PD6","tg_group":"Westerly","frequency":null,"metadata":null,"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"99272490-106d-4f86-8312-6f60d35772c6","system_id":197,"tgid":1063,"name":"Fire Operations","alpha_tag":"Westerly FD","tg_group":"Westerly","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"dfc744b0-adbf-45dc-b118-c4f2b06cfaf0","system_id":197,"tgid":1072,"name":"Police/Fire/EMS Ops","alpha_tag":"Westerly PFE","tg_group":"Westerly","frequency":null,"metadata":null,"tags":["Multi-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"77881d73-3a5e-443b-bc46-976647d1c1d3","system_id":197,"tgid":1082,"name":"EMS Operations","alpha_tag":"Westerly EMS ","tg_group":"Westerly","frequency":null,"metadata":null,"tags":["EMS Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"f8f6237c-6218-4a86-bb47-080b1f796613","system_id":197,"tgid":1363,"name":"Police 1 - Dispatch","alpha_tag":"Woonskt PD 1","tg_group":"Woonsocket","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"7667bd66-6166-4c43-b75b-63390b514bbe","system_id":197,"tgid":1364,"name":"Police 2","alpha_tag":"Woonskt PD 2","tg_group":"Woonsocket","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"491aa46b-524b-4e1c-9b74-56255fb214c3","system_id":197,"tgid":1360,"name":"Fire Dispatch - Operations","alpha_tag":"Woonsocket FD D","tg_group":"Woonsocket","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"f74907b7-ce1c-4a94-a10b-78b5e68f049f","system_id":197,"tgid":1361,"name":"Fire Secondary","alpha_tag":"Woonsocket FD 2","tg_group":"Woonsocket","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"cb002b96-a5d3-4d59-9f6e-977d587abb42","system_id":197,"tgid":1354,"name":"Fire - Fireground 3","alpha_tag":"Woonskt FD 3","tg_group":"Woonsocket","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"d0972d5f-3ffc-498b-bcbe-c26f10425576","system_id":197,"tgid":1367,"name":"Citywide","alpha_tag":"Woonskt City","tg_group":"Woonsocket","frequency":null,"metadata":null,"tags":["Multi-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"1aee1b8a-232d-4035-85dd-276ee1f43c8c","system_id":197,"tgid":1368,"name":"Public Works - Streets","alpha_tag":"Woonsocket PW","tg_group":"Woonsocket","frequency":null,"metadata":null,"tags":["Public Works"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"d7e92a99-3eb1-4107-902f-59ba75f8dd14","system_id":197,"tgid":1,"name":"RISCON Radio Technicians","alpha_tag":"Radio Techs","tg_group":"Radio Technicians","frequency":null,"metadata":null,"tags":["Public Works"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":"42ee3778-6ddb-402d-ab88-dd0ebdbf229f","system_id":197,"tgid":10125,"name":"RISCON Radio Technicians","alpha_tag":"Radio Techs","tg_group":"Radio Technicians","frequency":null,"metadata":null,"tags":["Public Works"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false}] \ No newline at end of file +[{"id":0,"system_id":197,"tgid":2,"name":"Intercity Fire","alpha_tag":"Intercity FD","tg_group":"Statewide Mutual Aid/Intersystem","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":3,"name":"Intercity Police","alpha_tag":"Intercity PD","tg_group":"Statewide Mutual Aid/Intersystem","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":21,"name":"North Dispatch ","alpha_tag":"RISP N Disp","tg_group":"State Police - District A (North)","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":22,"name":"North Car-to-Car/Information","alpha_tag":"RISP N Car","tg_group":"State Police - District A (North)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":24,"name":"North Tactical Ops 1","alpha_tag":"RISP N Tac 1","tg_group":"State Police - District A (North)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":23,"name":"North Tactical Ops 2","alpha_tag":"RISP N Tac 2","tg_group":"State Police - District A (North)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":25,"name":"South Dispatch ","alpha_tag":"RISP S Disp","tg_group":"State Police - District B (South)","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":27,"name":"South Car-to-Car/Information","alpha_tag":"RISP S Car","tg_group":"State Police - District B (South)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":16,"name":"State Fire Marshall","alpha_tag":"State FMO","tg_group":"Statewide Fire","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1038,"name":"Northern Rhode Island Fire Chiefs","alpha_tag":"NRI Fire Chi","tg_group":"Statewide Fire","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1041,"name":"Southern Rhode Island Fire Chiefs","alpha_tag":"SRI Fire Chi","tg_group":"Statewide Fire","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1314,"name":"Tanker Taskforce 1","alpha_tag":"Tanker TF 1","tg_group":"Statewide Fire","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":194,"name":"Lifepact Ambulance (Statewide)","alpha_tag":"Lifepact Amb","tg_group":"Statewide EMS and Hospitals","frequency":null,"metadata":null,"tags":["EMS Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":212,"name":"Fatima St Josephs","alpha_tag":"Fatima-St Joes","tg_group":"Statewide EMS and Hospitals","frequency":null,"metadata":null,"tags":["Business"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":220,"name":"Health 1","alpha_tag":"Health 1","tg_group":"Statewide EMS and Hospitals","frequency":null,"metadata":{"encrypted":true},"tags":["EMS-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":221,"name":"Health 2","alpha_tag":"Health 2","tg_group":"Statewide EMS and Hospitals","frequency":null,"metadata":{"encrypted":true},"tags":["EMS-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":222,"name":"Department of Health - Statewide","alpha_tag":"Dept of HealthSW","tg_group":"Statewide EMS and Hospitals","frequency":null,"metadata":null,"tags":["EMS-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":228,"name":"DMAT South","alpha_tag":"DMAT South","tg_group":"Statewide EMS and Hospitals","frequency":null,"metadata":{"encrypted":true},"tags":["Emergency Ops"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":232,"name":"Life Span Net 1","alpha_tag":"Life Span 1","tg_group":"Statewide EMS and Hospitals","frequency":null,"metadata":null,"tags":["EMS-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":234,"name":"RI Hospital Operations","alpha_tag":"RI Hosp Ops","tg_group":"Statewide EMS and Hospitals","frequency":null,"metadata":null,"tags":["Business"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":120,"name":"Law Enforcement Operations","alpha_tag":"DEM PD Ops","tg_group":"Department of Environmental Management","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":122,"name":"Law Enforcement Police","alpha_tag":"DEM Police","tg_group":"Department of Environmental Management","frequency":null,"metadata":null,"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10,"name":"Emergency Management Agency 1","alpha_tag":"EMA-1","tg_group":"Emergency Management Agency","frequency":null,"metadata":null,"tags":["Emergency Ops"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":20,"name":"Emergency Management Agency","alpha_tag":"EMA","tg_group":"Emergency Management Agency","frequency":null,"metadata":null,"tags":["Emergency Ops"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":4,"name":"Wide Area 3","alpha_tag":"Wide Area 3","tg_group":"Statewide Area/Events","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":5,"name":"Wide Area 4","alpha_tag":"Wide Area 4","tg_group":"Statewide Area/Events","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":6,"name":"Wide Area 5","alpha_tag":"Wide Area 5","tg_group":"Statewide Area/Events","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":7,"name":"Wide Area 6","alpha_tag":"Wide Area 6","tg_group":"Statewide Area/Events","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1018,"name":"Southwide CH-1","alpha_tag":"SOUTHWIDE 1","tg_group":"Statewide Area/Events","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1019,"name":"Southwide CH-2","alpha_tag":"SOUTHWIDE 2","tg_group":"Statewide Area/Events","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1022,"name":"Wide Area 7","alpha_tag":"WIDE AREA 7","tg_group":"Statewide Area/Events","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1023,"name":"Wide Area 8","alpha_tag":"WIDE AREA 8","tg_group":"Statewide Area/Events","frequency":null,"metadata":{"encrypted":true},"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1025,"name":"Inland Marine Interop","alpha_tag":"Inland Marine IO","tg_group":"Statewide Area/Events","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1037,"name":"Southside CH 5","alpha_tag":"SOUTHSIDE 5","tg_group":"Statewide Area/Events","frequency":null,"metadata":{"encrypted":true},"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1173,"name":"North Wide 1","alpha_tag":"NORTHWIDE1","tg_group":"Statewide Area/Events","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1174,"name":"North Wide 2","alpha_tag":"NORTHWIDE2","tg_group":"Statewide Area/Events","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1177,"name":"North Wide 5","alpha_tag":"NORTHWIDE5","tg_group":"Statewide Area/Events","frequency":null,"metadata":{"encrypted":true},"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1185,"name":"Metro Wide 1","alpha_tag":"METROWIDE1","tg_group":"Statewide Area/Events","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1186,"name":"Metro Wide 2","alpha_tag":"METROWIDE2","tg_group":"Statewide Area/Events","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1187,"name":"Metro Wide 3","alpha_tag":"METROWIDE3","tg_group":"Statewide Area/Events","frequency":null,"metadata":{"encrypted":true},"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1335,"name":"East Wide 1","alpha_tag":"EASTWIDE 1","tg_group":"Statewide Area/Events","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1336,"name":"East Wide 2","alpha_tag":"EASTWIDE 2","tg_group":"Statewide Area/Events","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1337,"name":"East Wide 3","alpha_tag":"EASTWIDE 3","tg_group":"Statewide Area/Events","frequency":null,"metadata":{"encrypted":true},"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":11186,"name":"Metro Wide 2","alpha_tag":"METROWIDE2","tg_group":"Statewide Area/Events","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1033,"name":"Tanker Taskforce ","alpha_tag":"TANK TF","tg_group":"Statewide Emergency Response","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1034,"name":"Hazmat 1","alpha_tag":"HZT DC1","tg_group":"Statewide Emergency Response","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1035,"name":"Hazmat 2","alpha_tag":"HZT DC2","tg_group":"Statewide Emergency Response","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":176,"name":"Department of Transportation - Primary","alpha_tag":"RIDOT Primary","tg_group":"Department of Transportation","frequency":null,"metadata":null,"tags":["Public Works"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":4421,"name":"Newport Pell Bridge Operations","alpha_tag":"RITBA - Pell Bdg","tg_group":"Tunnel and Bridge Authority","frequency":null,"metadata":null,"tags":["Public Works"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":274,"name":"Providence VA Police","alpha_tag":"VA Police","tg_group":"Federal","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":186,"name":"Rhode Island Public Transit Auth.","alpha_tag":"RIPTA","tg_group":"RIPTA","frequency":null,"metadata":{"encrypted":true},"tags":["Transportation"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":187,"name":"Rhode Island Public Transit Auth.","alpha_tag":"RIPTA","tg_group":"RIPTA","frequency":null,"metadata":null,"tags":["Transportation"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":188,"name":"Rhode Island Public Transit Auth.","alpha_tag":"RIPTA","tg_group":"RIPTA","frequency":null,"metadata":null,"tags":["Transportation"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":189,"name":"Rhode Island Public Transit Auth.","alpha_tag":"RIPTA","tg_group":"RIPTA","frequency":null,"metadata":null,"tags":["Transportation"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":190,"name":"Rhode Island Public Transit. Auth.","alpha_tag":"RIPTA","tg_group":"RIPTA","frequency":null,"metadata":null,"tags":["Transportation"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":304,"name":"Fire Operations","alpha_tag":"Quonset ANGB FD","tg_group":"Quonset ANGB","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":17,"name":"Airport Police Operations","alpha_tag":"TF Green PD","tg_group":"Rhode Island Airport Commission","frequency":null,"metadata":{"encrypted":true},"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":19,"name":"Airport Fire Operations","alpha_tag":"TF Green FD","tg_group":"Rhode Island Airport Commission","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1126,"name":"University of Rhode Island Police - Dispatch","alpha_tag":"URI PD","tg_group":"College/Education Security","frequency":null,"metadata":{"encrypted":true},"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1131,"name":"University of Rhode Island - EMS","alpha_tag":"URI EMS","tg_group":"College/Education Security","frequency":null,"metadata":{"encrypted":true},"tags":["EMS Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1348,"name":"St. George's School (Middletown) - Security","alpha_tag":"St George Sec","tg_group":"College/Education Security","frequency":null,"metadata":null,"tags":["Security"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10228,"name":"Rhode Island School of Design - Security","alpha_tag":"RISD Secuty","tg_group":"College/Education Security","frequency":null,"metadata":{"encrypted":true},"tags":["Security"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10229,"name":"Providence College Security - Dispatch","alpha_tag":"PROV COLL","tg_group":"College/Education Security","frequency":null,"metadata":{"encrypted":true},"tags":["Security"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10230,"name":"Rhode Island College Security","alpha_tag":"RI COL SEC","tg_group":"College/Education Security","frequency":null,"metadata":null,"tags":["Security"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":11001,"name":"Brown University Police - Dispatch","alpha_tag":"BROWN UNIV","tg_group":"College/Education Security","frequency":null,"metadata":{"encrypted":true},"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":11002,"name":"Brown University Police - Car-to-Car","alpha_tag":"BROWN CAR","tg_group":"College/Education Security","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":11003,"name":"Brown University Police - Tactical","alpha_tag":"BROWN TAC","tg_group":"College/Education Security","frequency":null,"metadata":{"encrypted":true},"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":12,"name":"Metro Wide 2","alpha_tag":"METROWIDE2","tg_group":"Statewide Misc.","frequency":null,"metadata":{"encrypted":true},"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":14,"name":"Metro Wide 4","alpha_tag":"METROWIDE4","tg_group":"Statewide Misc.","frequency":null,"metadata":{"encrypted":true},"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":70,"name":"RI Traffic Tribunal Security","alpha_tag":"TFC TRIBUNAL","tg_group":"Statewide Misc.","frequency":null,"metadata":{"encrypted":true},"tags":["Security"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":168,"name":"Rhode Island Red Cross - Primary","alpha_tag":"Red Cross 1","tg_group":"Statewide Misc.","frequency":null,"metadata":null,"tags":["Other"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":169,"name":"Rhode Island Red Cross - Secondary","alpha_tag":"Red Cross 2","tg_group":"Statewide Misc.","frequency":null,"metadata":null,"tags":["Other"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":223,"name":"Statewide Nursing Homes Net","alpha_tag":"NURSING HM","tg_group":"Statewide Misc.","frequency":null,"metadata":null,"tags":["Other"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":243,"name":"Hospital Operations","alpha_tag":"Slater Hosp Ops","tg_group":"Statewide Misc.","frequency":null,"metadata":null,"tags":["Business"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":244,"name":"Slater Hospital Security","alpha_tag":"Slater Hosp Sec","tg_group":"Statewide Misc.","frequency":null,"metadata":null,"tags":["Security"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1042,"name":"County Fireground","alpha_tag":"WashCo FireG","tg_group":"Washington County","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1479,"name":"County Fire Station/Station","alpha_tag":"WashCo FireS","tg_group":"Washington County","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1712,"name":"Fire 1 Dispatch","alpha_tag":"BarringtnFD1","tg_group":"Barrington","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1713,"name":"Fire 2","alpha_tag":"BarringtnFD2","tg_group":"Barrington","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1715,"name":"Police Operations","alpha_tag":"BarringtonPD 1","tg_group":"Barrington","frequency":null,"metadata":{"encrypted":true},"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1716,"name":"Police Secondary","alpha_tag":"BarringtonPD 2","tg_group":"Barrington","frequency":null,"metadata":null,"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1744,"name":"Fire Operations (Patch from VHF)","alpha_tag":"Bristol FD","tg_group":"Bristol","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1755,"name":"Harbormaster","alpha_tag":"Bristol Harbor","tg_group":"Bristol","frequency":null,"metadata":null,"tags":["Public Works"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":2003,"name":"Police","alpha_tag":"Burrville PD","tg_group":"Burrillville","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":2004,"name":"Police 2","alpha_tag":"Burrvl PD2","tg_group":"Burrillville","frequency":null,"metadata":null,"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":2005,"name":"Police 3 Detectives","alpha_tag":"Burrvl PD3","tg_group":"Burrillville","frequency":null,"metadata":{"encrypted":true},"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":2006,"name":"Police 4","alpha_tag":"Burrvl PD4","tg_group":"Burrillville","frequency":null,"metadata":null,"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":2000,"name":"Fire Misc (Ops are VHF)","alpha_tag":"Burrvl FD","tg_group":"Burrillville","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":2001,"name":"Fire TAC-1","alpha_tag":"Burvl FDTAC1","tg_group":"Burrillville","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":2009,"name":"Fire TAC-2","alpha_tag":"Burvl FDTAC2","tg_group":"Burrillville","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":2002,"name":"EMS Misc (Ops are VHF)","alpha_tag":"Burrvl EMS","tg_group":"Burrillville","frequency":null,"metadata":null,"tags":["EMS-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":2007,"name":"Town-Wide","alpha_tag":"Burrvl Town","tg_group":"Burrillville","frequency":null,"metadata":null,"tags":["Multi-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":2008,"name":"Emergency Management","alpha_tag":"Burrvl EMA","tg_group":"Burrillville","frequency":null,"metadata":null,"tags":["Emergency Ops"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1838,"name":"Police 1 Dispatch","alpha_tag":"CentFallsPD1","tg_group":"Central Falls","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1839,"name":"Police 2","alpha_tag":"CentFallsPD2","tg_group":"Central Falls","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1835,"name":"Fire Dispatch (Simulcast of UHF)","alpha_tag":"CentFalls FD 1","tg_group":"Central Falls","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1836,"name":"Fireground","alpha_tag":"CentFalls FD 2","tg_group":"Central Falls","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1425,"name":"Police Operations - Simulcast of UHF","alpha_tag":"CharlestownPD","tg_group":"Charlestown","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1429,"name":"EMS - Linked to 151.3325","alpha_tag":"Chastown EMS","tg_group":"Charlestown","frequency":null,"metadata":null,"tags":["EMS Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1483,"name":"Police 1 - Dispatch","alpha_tag":"Coventry PD","tg_group":"Coventry","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1484,"name":"Police 2","alpha_tag":"Coventry PD2","tg_group":"Coventry","frequency":null,"metadata":null,"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1480,"name":"Fire","alpha_tag":"Coventry FD","tg_group":"Coventry","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1500,"name":"Fire - Dispatch/Operations","alpha_tag":"Cranston FD Disp","tg_group":"Cranston","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1501,"name":"Fire - Fireground 2","alpha_tag":"Cranston FD FG2","tg_group":"Cranston","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1502,"name":"Fire - Fireground 3","alpha_tag":"Cranston FD FG3","tg_group":"Cranston","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1503,"name":"Fire - Fireground 4","alpha_tag":"Cranston FD FG4","tg_group":"Cranston","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1504,"name":"Fire - Admin/Alt Fireground 5","alpha_tag":"Cranston FD Admi","tg_group":"Cranston","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1520,"name":"Fire","alpha_tag":"Cumberland FD","tg_group":"Cumberland","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1523,"name":"Police Secondary","alpha_tag":"Cumberland PD","tg_group":"Cumberland","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1776,"name":"Fire Talk Around","alpha_tag":"E Greenwich F-TA","tg_group":"East Greenwich","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1779,"name":"Police Operations","alpha_tag":"E Greenwich PD","tg_group":"East Greenwich","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1869,"name":"Police 1 - Dispatch","alpha_tag":"E Prov PD 1","tg_group":"East Providence","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1872,"name":"Police 2","alpha_tag":"E Prov PD 2","tg_group":"East Providence","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1870,"name":"Police 3","alpha_tag":"E Prov PD 3","tg_group":"East Providence","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1883,"name":"Detectives","alpha_tag":"E Prov PD12","tg_group":"East Providence","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1866,"name":"Fire - Dispatch/Operations","alpha_tag":"E Prov FD 1","tg_group":"East Providence","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1867,"name":"Fire \"Channel 2\"","alpha_tag":"E Prov FD 2","tg_group":"East Providence","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1878,"name":"Fire \"Channel 3\"","alpha_tag":"E Prov FD 3","tg_group":"East Providence","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":2064,"name":"Fire - Fireground","alpha_tag":"Exeter FD-G","tg_group":"Exeter","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1904,"name":"Fire","alpha_tag":"Foster Fire","tg_group":"Foster","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1939,"name":"Police","alpha_tag":"Glocester PD","tg_group":"Glocester","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1940,"name":"Police Secondary","alpha_tag":"Glocester PD 2","tg_group":"Glocester","frequency":null,"metadata":null,"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1410,"name":"Police","alpha_tag":"Hopkinton PD","tg_group":"Hopkinton","frequency":null,"metadata":{"encrypted":true},"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1100,"name":"Police 1 - Dispatch","alpha_tag":"Jamestown PD 1","tg_group":"Jamestown","frequency":null,"metadata":{"encrypted":true},"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1101,"name":"Police 2","alpha_tag":"Jamestown PD 2","tg_group":"Jamestown","frequency":null,"metadata":{"encrypted":true},"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1108,"name":"Fire","alpha_tag":"Jamestown FD","tg_group":"Jamestown","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1120,"name":"Fireground 1","alpha_tag":"Jamestown FG 1","tg_group":"Jamestown","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1121,"name":"Fireground 2","alpha_tag":"Jamestown FG 2","tg_group":"Jamestown","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1114,"name":"Public Works","alpha_tag":"Jamestown DPW","tg_group":"Jamestown","frequency":null,"metadata":null,"tags":["Public Works"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1107,"name":"Town Schools","alpha_tag":"Jamestown School","tg_group":"Jamestown","frequency":null,"metadata":null,"tags":["Schools"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1619,"name":"Police Operations","alpha_tag":"Johnston PD","tg_group":"Johnston","frequency":null,"metadata":{"encrypted":true},"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1616,"name":"Fire Operations","alpha_tag":"Johnston FD","tg_group":"Johnston","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1617,"name":"Fireground","alpha_tag":"Johnston FG","tg_group":"Johnston","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1683,"name":"Police F1","alpha_tag":"Lincoln Police","tg_group":"Lincoln","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1684,"name":"Police F2","alpha_tag":"Lincoln Police 2","tg_group":"Lincoln","frequency":null,"metadata":null,"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1680,"name":"Fire Dispatch","alpha_tag":"Lincoln Fire 1","tg_group":"Lincoln","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1681,"name":"Fireground 2","alpha_tag":"Lincoln Fire 2","tg_group":"Lincoln","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1691,"name":"Fireground 3","alpha_tag":"Lincoln Fire 3","tg_group":"Lincoln","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1682,"name":"EMS","alpha_tag":"Lincoln EMS","tg_group":"Lincoln","frequency":null,"metadata":null,"tags":["EMS Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1688,"name":"Emergency Management","alpha_tag":"Lincoln EMA","tg_group":"Lincoln","frequency":null,"metadata":null,"tags":["Emergency Ops"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1687,"name":"Townwide","alpha_tag":"Lincoln Townwide","tg_group":"Lincoln","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1692,"name":"Public Works","alpha_tag":"Lincoln DPW","tg_group":"Lincoln","frequency":null,"metadata":null,"tags":["Public Works"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1264,"name":"Police","alpha_tag":"LittleCompPD","tg_group":"Little Compton","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1266,"name":"Fire","alpha_tag":"LittleCompFD","tg_group":"Little Compton","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1338,"name":"Police Operations","alpha_tag":"MiddletownPD","tg_group":"Middletown","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1343,"name":"Fire Operations","alpha_tag":"Middletown FD","tg_group":"Middletown","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1345,"name":"Townwide","alpha_tag":"MiddletownTW","tg_group":"Middletown","frequency":null,"metadata":null,"tags":["Multi-Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1001,"name":"Police - Dispatch","alpha_tag":"Narrag PD 1","tg_group":"Narragansett","frequency":null,"metadata":{"encrypted":true},"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1002,"name":"Police - Car/Car","alpha_tag":"Narrag PD 2","tg_group":"Narragansett","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1003,"name":"Police - Special Details 1/Town Beaches","alpha_tag":"Narrag PD 3","tg_group":"Narragansett","frequency":null,"metadata":{"encrypted":true},"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1004,"name":"Police - Special Details 2","alpha_tag":"Narrag PD 4","tg_group":"Narragansett","frequency":null,"metadata":{"encrypted":true},"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1005,"name":"Police - Harbormaster","alpha_tag":"Narrag PD 5","tg_group":"Narragansett","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1007,"name":"Police - Detectives","alpha_tag":"Narrag PD 7","tg_group":"Narragansett","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1008,"name":"Police - Detectives","alpha_tag":"Narrag PD 8","tg_group":"Narragansett","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1006,"name":"Fire - Dispatch","alpha_tag":"Narrag FD","tg_group":"Narragansett","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1012,"name":"Fire - Fireground 1","alpha_tag":"Narrag FDFG1","tg_group":"Narragansett","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1013,"name":"Fire - Fireground 2","alpha_tag":"Narrag FDFG2","tg_group":"Narragansett","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1016,"name":"Fire - Administration","alpha_tag":"Narrag FD AD","tg_group":"Narragansett","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1014,"name":"Fire - EMS Ops","alpha_tag":"Narrag EMS","tg_group":"Narragansett","frequency":null,"metadata":null,"tags":["EMS Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1017,"name":"Public Works","alpha_tag":"Narrag DPW","tg_group":"Narragansett","frequency":null,"metadata":null,"tags":["Public Works"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1010,"name":"Town Administration","alpha_tag":"Narrag TownA","tg_group":"Narragansett","frequency":null,"metadata":null,"tags":["Other"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1011,"name":"Townwide Interop","alpha_tag":"Narrag IOP","tg_group":"Narragansett","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1376,"name":"Police","alpha_tag":"New Shore PD","tg_group":"New Shoreham","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1300,"name":"Police 1 - Dispatch","alpha_tag":"Newport PD 1","tg_group":"Newport","frequency":null,"metadata":{"encrypted":true},"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1302,"name":"Police 2 - Records","alpha_tag":"Newport PD 2","tg_group":"Newport","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1304,"name":"Police 4 - Tactical 1","alpha_tag":"Newport PD 4","tg_group":"Newport","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1307,"name":"Police 7 - Tactical 4","alpha_tag":"Newport PD 7","tg_group":"Newport","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1308,"name":"Police 8 - Tactical 5","alpha_tag":"Newport PD 8","tg_group":"Newport","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1303,"name":"Fire Dispatch/Operations","alpha_tag":"Newport FD1","tg_group":"Newport","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1305,"name":"Fireground Ops 1","alpha_tag":"Newport FG1","tg_group":"Newport","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1306,"name":"Fireground Ops 2","alpha_tag":"Newport FG2","tg_group":"Newport","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1301,"name":"Fire - Training","alpha_tag":"Newport FDT","tg_group":"Newport","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1291,"name":"Water Department","alpha_tag":"Newport Water","tg_group":"Newport","frequency":null,"metadata":null,"tags":["Public Works"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1293,"name":"Public Works","alpha_tag":"Newport DPW","tg_group":"Newport","frequency":null,"metadata":null,"tags":["Public Works"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1297,"name":"Citywide Events","alpha_tag":"Newport Evnt","tg_group":"Newport","frequency":null,"metadata":null,"tags":["Public Works"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1312,"name":"Newport Citywide","alpha_tag":"Newport CW","tg_group":"Newport","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1285,"name":"Police 1 - Dispatch","alpha_tag":"NKing PD 1","tg_group":"North Kingstown","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1286,"name":"Police 2 - Admin","alpha_tag":"NKing PD 2","tg_group":"North Kingstown","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1287,"name":"Police 3 - Car/Car","alpha_tag":"NKing PD 3","tg_group":"North Kingstown","frequency":null,"metadata":null,"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1280,"name":"Fire - Dispatch","alpha_tag":"NKing Fire D","tg_group":"North Kingstown","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1281,"name":"Fire - Fireground","alpha_tag":"NKing Fire G","tg_group":"North Kingstown","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1536,"name":"Police 1 - Dispatch","alpha_tag":"NorthPrv PD1","tg_group":"North Providence","frequency":null,"metadata":{"encrypted":true},"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1537,"name":"Police 2 - Car/Car","alpha_tag":"NorthPrv PD2","tg_group":"North Providence","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1538,"name":"Police 3 - Tactical","alpha_tag":"NorthPrv PD3","tg_group":"North Providence","frequency":null,"metadata":{"encrypted":true},"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1547,"name":"Fire Dispatch ","alpha_tag":"NorthPrv FDD","tg_group":"North Providence","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1548,"name":"Fire 2","alpha_tag":"NorthPrv Fire 2","tg_group":"North Providence","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1549,"name":"Fire 3","alpha_tag":"NorthPrv Fire 3","tg_group":"North Providence","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1550,"name":"Fire 4","alpha_tag":"NorthPrv Fire 4","tg_group":"North Providence","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1551,"name":"Fire 5","alpha_tag":"NorthPrv Fire 5","tg_group":"North Providence","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1552,"name":"Fire 6","alpha_tag":"NorthPrv Fire 6","tg_group":"North Providence","frequency":null,"metadata":{"encrypted":true},"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1544,"name":"Townwide 1","alpha_tag":"NorthPrv TownW 1","tg_group":"North Providence","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1545,"name":"Townwide 2","alpha_tag":"NorthPrv TownW 2","tg_group":"North Providence","frequency":null,"metadata":null,"tags":["Interop"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1554,"name":"Public Works","alpha_tag":"NorthPrv DPW","tg_group":"North Providence","frequency":null,"metadata":null,"tags":["Public Works"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1971,"name":"Police","alpha_tag":"N Smithfd PD","tg_group":"North Smithfield","frequency":null,"metadata":{"encrypted":true},"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1968,"name":"Fire Dispatch/Operations","alpha_tag":"N Smithfield FD","tg_group":"North Smithfield","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1969,"name":"Fire Secondary","alpha_tag":"N Smithfield FD2","tg_group":"North Smithfield","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1981,"name":"Fireground","alpha_tag":"N Smithfield FD3","tg_group":"North Smithfield","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1440,"name":"Fire - Operations","alpha_tag":"Pawtucket FD 1","tg_group":"Pawtucket","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1441,"name":"Fireground","alpha_tag":"Pawtucket FG","tg_group":"Pawtucket","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1442,"name":"EMS Tac","alpha_tag":"Pawtucket EMSTac","tg_group":"Pawtucket","frequency":null,"metadata":null,"tags":["EMS-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1248,"name":"Police","alpha_tag":"PortsmouthPD","tg_group":"Portsmouth","frequency":null,"metadata":{"encrypted":true},"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1253,"name":"Fire Dispatch (Patch to VHF Primary)","alpha_tag":"Portsmouth FD","tg_group":"Portsmouth","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1255,"name":"Fireground","alpha_tag":"Portsmouth FG","tg_group":"Portsmouth","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1262,"name":"Island Fire Dispatch","alpha_tag":"Prudence Isl FD","tg_group":"Portsmouth","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10000,"name":"Police - All Call - Emergency Broadcasts","alpha_tag":"PPD ATG","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Emergency Ops"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10001,"name":"Police 1 - Dispatch","alpha_tag":"PPD CH 1","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10002,"name":"Police 2","alpha_tag":"PPD CH 2","tg_group":"Providence (City)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10003,"name":"Police 3","alpha_tag":"PPD CH 3","tg_group":"Providence (City)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10004,"name":"Police 4","alpha_tag":"PPD CH-4","tg_group":"Providence (City)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10005,"name":"Police 5 -Detectives 1","alpha_tag":"PPD DETEC 1","tg_group":"Providence (City)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10006,"name":"Police 6 - Car-to-Car","alpha_tag":"PPD T/A","tg_group":"Providence (City)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10007,"name":"Police 7 - Narcotics 1","alpha_tag":"PPD NARC 1","tg_group":"Providence (City)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10008,"name":"Police 8 - Narcotics 2","alpha_tag":"PPD NARC 2","tg_group":"Providence (City)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10009,"name":"Police 9 - Detectives 2","alpha_tag":"PPD DETEC 2","tg_group":"Providence (City)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10010,"name":"Police 10 - Special Details 1","alpha_tag":"PPD DETAIL 1","tg_group":"Providence (City)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10011,"name":"Police 11 - Special Details 2","alpha_tag":"PPD DETAIL 2","tg_group":"Providence (City)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10012,"name":"Police 12 - Corrections Security","alpha_tag":"PPD CORR SEC","tg_group":"Providence (City)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10013,"name":"Police 13 - Special Response Unit","alpha_tag":"PPD SRU","tg_group":"Providence (City)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10014,"name":"Police 14 - Administration","alpha_tag":"PPD ADMIN","tg_group":"Providence (City)","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10100,"name":"Fire All Call - Emergency Broadcasts","alpha_tag":"PROV FD ATG","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Emergency Ops"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10101,"name":"Fire Dispatch","alpha_tag":"PFD DISPATCH","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10107,"name":"Fireground 2","alpha_tag":"PFD CH-2 FG","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10108,"name":"Fireground 3","alpha_tag":"PFD CH-3 FG","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10109,"name":"Fireground 4","alpha_tag":"PFD CH-4 FG","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10102,"name":"Fire 5","alpha_tag":"PFD CH-5","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10103,"name":"Fire 6","alpha_tag":"PFD CH-6","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10104,"name":"Fire 7","alpha_tag":"PFD CH-7","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10110,"name":"Fire - Mutual Aid 1","alpha_tag":"PFD M/A 1","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10111,"name":"Fire - Mutual Aid 2","alpha_tag":"PFD M/A 2","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10112,"name":"Fire - Mutual Aid 3","alpha_tag":"PFD M/A 3","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10113,"name":"Fireground 8","alpha_tag":"PFD Fireground 8","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10105,"name":"Fire - Administration","alpha_tag":"PFD ADMIN","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10106,"name":"Fire - Communications","alpha_tag":"PFD COMM","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10207,"name":"Public Works","alpha_tag":"PROV DPW","tg_group":"Providence (City)","frequency":null,"metadata":null,"tags":["Public Works"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":2035,"name":"Police","alpha_tag":"Richmond PD","tg_group":"Richmond","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":2042,"name":"Chariho Regional High School","alpha_tag":"Chariho Reg HS","tg_group":"Richmond","frequency":null,"metadata":null,"tags":["Schools"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1460,"name":"Police","alpha_tag":"Scituate PD","tg_group":"Scituate","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1463,"name":"Fire Operations","alpha_tag":"Scituate FD","tg_group":"Scituate","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1651,"name":"Police Operations","alpha_tag":"SmithfieldPD","tg_group":"Smithfield","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1652,"name":"Police Secondary","alpha_tag":"Smfld PD 2","tg_group":"Smithfield","frequency":null,"metadata":null,"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1653,"name":"Police Detectives","alpha_tag":"Smfld PD Det","tg_group":"Smithfield","frequency":null,"metadata":{"encrypted":true},"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1654,"name":"Police Admin","alpha_tag":"Smfld PD Adm","tg_group":"Smithfield","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1661,"name":"Police Details","alpha_tag":"Smfld PD Dtl","tg_group":"Smithfield","frequency":null,"metadata":null,"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1648,"name":"Fire - Fireground","alpha_tag":"SmithfieldFD","tg_group":"Smithfield","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1655,"name":"Town-Wide","alpha_tag":"Smfld Town","tg_group":"Smithfield","frequency":null,"metadata":null,"tags":["Multi-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1657,"name":"Emergency Management","alpha_tag":"Smfld EMA","tg_group":"Smithfield","frequency":null,"metadata":null,"tags":["Emergency Ops"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1660,"name":"Public Works","alpha_tag":"Smfld DPW","tg_group":"Smithfield","frequency":null,"metadata":null,"tags":["Public Works"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1225,"name":"Police 1 - Dispatch","alpha_tag":"SKing PD 1","tg_group":"South Kingstown","frequency":null,"metadata":{"encrypted":true},"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1226,"name":"Police 2 - Car/Car","alpha_tag":"SKing PD 2","tg_group":"South Kingstown","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1235,"name":"Police 3 - Tactical","alpha_tag":"SKing PD 3","tg_group":"South Kingstown","frequency":null,"metadata":{"encrypted":true},"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1236,"name":"Police 5 - Tactical","alpha_tag":"SKing PD 5","tg_group":"South Kingstown","frequency":null,"metadata":{"encrypted":true},"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1232,"name":"Fire - UHF Simulcast","alpha_tag":"SKing FD Lnk","tg_group":"South Kingstown","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1240,"name":"Fire - Detail","alpha_tag":"SKing Fire D","tg_group":"South Kingstown","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1227,"name":"Union Fire District - Fireground 1","alpha_tag":"UnionFD FG 1","tg_group":"South Kingstown","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1237,"name":"Union Fire District - Fireground 2","alpha_tag":"UnionFD FG 2","tg_group":"South Kingstown","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1026,"name":"Union Fire District - Special Events","alpha_tag":"UnionFD Evnt","tg_group":"South Kingstown","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1015,"name":"EMS","alpha_tag":"SKing EMS","tg_group":"South Kingstown","frequency":null,"metadata":{"encrypted":true},"tags":["EMS Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1316,"name":"Police (Simulcast 482.9625)","alpha_tag":"Tiverton PD","tg_group":"Tiverton","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1315,"name":"Fire (Simulcast 471.7875)","alpha_tag":"Tiverton FD","tg_group":"Tiverton","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1162,"name":"Fire","alpha_tag":"Warwick FD","tg_group":"Warwick","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1170,"name":"Fireground","alpha_tag":"Warwick FG","tg_group":"Warwick","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1805,"name":"Police","alpha_tag":"W Greenwh PD","tg_group":"West Greenwich","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1806,"name":"Police Secondary","alpha_tag":"W GreenwichPD2","tg_group":"West Greenwich","frequency":null,"metadata":null,"tags":["Law Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1208,"name":"Fire Operations","alpha_tag":"W Warwick FD","tg_group":"West Warwick","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1050,"name":"Police 1 - Dispatch","alpha_tag":"Westerly PD1","tg_group":"Westerly","frequency":null,"metadata":{"encrypted":true},"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1051,"name":"Police 2","alpha_tag":"Westerly PD2","tg_group":"Westerly","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1052,"name":"Police 3","alpha_tag":"Westerly PD3","tg_group":"Westerly","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1053,"name":"Police 4","alpha_tag":"Westerly PD4","tg_group":"Westerly","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1054,"name":"Police 5 - Reserve Officers","alpha_tag":"Westerly PD5","tg_group":"Westerly","frequency":null,"metadata":null,"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1064,"name":"Police 6 - Traffic Division","alpha_tag":"Westerly PD6","tg_group":"Westerly","frequency":null,"metadata":null,"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1063,"name":"Fire Operations","alpha_tag":"Westerly FD","tg_group":"Westerly","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1072,"name":"Police/Fire/EMS Ops","alpha_tag":"Westerly PFE","tg_group":"Westerly","frequency":null,"metadata":null,"tags":["Multi-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1082,"name":"EMS Operations","alpha_tag":"Westerly EMS ","tg_group":"Westerly","frequency":null,"metadata":null,"tags":["EMS Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1363,"name":"Police 1 - Dispatch","alpha_tag":"Woonskt PD 1","tg_group":"Woonsocket","frequency":null,"metadata":null,"tags":["Law Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1364,"name":"Police 2","alpha_tag":"Woonskt PD 2","tg_group":"Woonsocket","frequency":null,"metadata":{"encrypted":true},"tags":["Law Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1360,"name":"Fire Dispatch - Operations","alpha_tag":"Woonsocket FD D","tg_group":"Woonsocket","frequency":null,"metadata":null,"tags":["Fire-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1361,"name":"Fire Secondary","alpha_tag":"Woonsocket FD 2","tg_group":"Woonsocket","frequency":null,"metadata":null,"tags":["Fire Dispatch"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1354,"name":"Fire - Fireground 3","alpha_tag":"Woonskt FD 3","tg_group":"Woonsocket","frequency":null,"metadata":null,"tags":["Fire-Tac"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1367,"name":"Citywide","alpha_tag":"Woonskt City","tg_group":"Woonsocket","frequency":null,"metadata":null,"tags":["Multi-Talk"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1368,"name":"Public Works - Streets","alpha_tag":"Woonsocket PW","tg_group":"Woonsocket","frequency":null,"metadata":null,"tags":["Public Works"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":1,"name":"RISCON Radio Technicians","alpha_tag":"Radio Techs","tg_group":"Radio Technicians","frequency":null,"metadata":null,"tags":["Public Works"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false},{"id":0,"system_id":197,"tgid":10125,"name":"RISCON Radio Technicians","alpha_tag":"Radio Techs","tg_group":"Radio Technicians","frequency":null,"metadata":null,"tags":["Public Works"],"alert":false,"alert_config":null,"weight":1,"system":{"id":197,"name":"RISCON"},"learned":false}] \ No newline at end of file diff --git a/sql/sqlc.yaml b/sql/sqlc.yaml index 4ce8915..c233814 100644 --- a/sql/sqlc.yaml +++ b/sql/sqlc.yaml @@ -23,10 +23,6 @@ sql: type: "UUID" - db_type: "pg_catalog.int4" go_type: "int" - - db_type: "pg_catalog.serial4" - go_type: "int" - - db_type: "integer" - go_type: "int" - db_type: "pg_catalog.timestamp" go_type: "time.Time" - db_type: "pg_catalog.text" From f7fdaf98675e97f5fcc656dbc4ce58c3aacf8fa0 Mon Sep 17 00:00:00 2001 From: Daniel Ponte Date: Tue, 19 Nov 2024 10:00:35 -0500 Subject: [PATCH 5/5] don't alert ignored --- pkg/alerting/alerting.go | 2 +- pkg/database/talkgroups.go | 2 +- pkg/database/talkgroups.sql.go | 8 ++++---- pkg/database/talkgroups.sql_test.go | 6 +++--- sql/postgres/queries/talkgroups.sql | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pkg/alerting/alerting.go b/pkg/alerting/alerting.go index 5061fca..811ad90 100644 --- a/pkg/alerting/alerting.go +++ b/pkg/alerting/alerting.go @@ -161,7 +161,7 @@ func (as *alerter) eval(ctx context.Context, now time.Time, testMode bool) ([]al for _, s := range as.scores { origScore := s.Score tgr, err := as.tgCache.TG(ctx, s.ID) - if err == nil && !tgr.Talkgroup.Alert { + if err != nil || !tgr.Talkgroup.Alert { continue } diff --git a/pkg/database/talkgroups.go b/pkg/database/talkgroups.go index a461dcd..ef8f8a6 100644 --- a/pkg/database/talkgroups.go +++ b/pkg/database/talkgroups.go @@ -51,7 +51,7 @@ 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, -TRUE, NULL::JSONB, 1.0, sys.id, sys.name, TRUE learned +NOT tgl.ignored, NULL::JSONB, 1.0, sys.id, sys.name, TRUE learned FROM talkgroups_learned tgl JOIN systems sys ON tgl.system_id = sys.id JOIN UNNEST($1::INT4[], $2::INT4[]) AS tgt(sys, tg) ON (tgl.system_id = tgt.sys AND tgl.tgid = tgt.tg);` diff --git a/pkg/database/talkgroups.sql.go b/pkg/database/talkgroups.sql.go index 5ee4fc3..05ba01d 100644 --- a/pkg/database/talkgroups.sql.go +++ b/pkg/database/talkgroups.sql.go @@ -162,7 +162,7 @@ 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, -TRUE, NULL::JSONB, 1.0, TRUE learned, sys.id, sys.name +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 = $1 AND tgl.tgid = $2 AND ignored IS NOT TRUE @@ -293,7 +293,7 @@ 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, -TRUE, NULL::JSONB, 1.0, TRUE learned, sys.id, sys.name +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 @@ -351,7 +351,7 @@ 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, -TRUE, NULL::JSONB, 1.0, TRUE learned, sys.id, sys.name +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 = $1 AND ignored IS NOT TRUE @@ -434,7 +434,7 @@ type UpdateTalkgroupParams struct { Alert *bool `json:"alert"` AlertConfig rules.AlertRules `json:"alert_config"` Weight *float32 `json:"weight"` - ID int `json:"id"` + ID *int32 `json:"id"` SystemID *int32 `json:"system_id"` TGID *int32 `json:"tgid"` } diff --git a/pkg/database/talkgroups.sql_test.go b/pkg/database/talkgroups.sql_test.go index 830c85b..a15f07e 100644 --- a/pkg/database/talkgroups.sql_test.go +++ b/pkg/database/talkgroups.sql_test.go @@ -17,7 +17,7 @@ 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, -TRUE, NULL::JSONB, 1.0, TRUE learned, sys.id, sys.name +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 = $1 AND tgl.tgid = $2 AND ignored IS NOT TRUE @@ -34,7 +34,7 @@ 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, -TRUE, NULL::JSONB, 1.0, TRUE learned, sys.id, sys.name +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 = $1 AND ignored IS NOT TRUE @@ -51,7 +51,7 @@ 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, -TRUE, NULL::JSONB, 1.0, TRUE learned, sys.id, sys.name +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 diff --git a/sql/postgres/queries/talkgroups.sql b/sql/postgres/queries/talkgroups.sql index 82a9be9..14b6ff2 100644 --- a/sql/postgres/queries/talkgroups.sql +++ b/sql/postgres/queries/talkgroups.sql @@ -35,7 +35,7 @@ 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, -TRUE, NULL::JSONB, 1.0, TRUE learned, sys.id, sys.name +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; @@ -51,7 +51,7 @@ 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, -TRUE, NULL::JSONB, 1.0, TRUE learned, sys.id, sys.name +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; @@ -67,7 +67,7 @@ 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, -TRUE, NULL::JSONB, 1.0, TRUE learned, sys.id, sys.name +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;