Merge pull request 'Pagination' (#49) from paginateTG into trunk

Reviewed-on: #49
This commit is contained in:
Daniel 2024-11-24 00:14:40 -05:00
commit 80c06919f8
15 changed files with 491 additions and 37 deletions

View file

@ -36,7 +36,7 @@ func PtrTo[T any](t T) *T {
return &t
}
func PtrOrNull[T comparable](val T) *T {
func NilIfZero[T comparable](val T) *T {
var zero T
if val == zero {
return nil
@ -45,7 +45,7 @@ func PtrOrNull[T comparable](val T) *T {
return &val
}
func ZeroOr[T any](v *T) T {
func ZeroIfNil[T any](v *T) T {
var zero T
if v == nil {
return zero
@ -53,3 +53,16 @@ func ZeroOr[T any](v *T) T {
return *v
}
func DefaultIfNilOrZero[T comparable](v *T, def T) T {
if v == nil {
return def
}
var zero T
if *v == zero {
return def
}
return *v
}

8
internal/forms/testdata/urlenc3.http vendored Normal file
View file

@ -0,0 +1,8 @@
POST /api/talkgroup/ HTTP/1.1
Host: xenon:3051
User-Agent: curl/8.10.1
Accept: */*
Content-Length: 16
Content-Type: application/x-www-form-urlencoded
page=1&perPage=2&orderBy=id

View file

@ -223,9 +223,17 @@ func (o *options) unmIterFields(r *http.Request, destStruct reflect.Value) error
}
destFieldVal.Set(reflect.ValueOf(ar))
default:
dvt := destFieldVal.Type()
if dvt.Kind() == reflect.Ptr {
dvt = dvt.Elem()
}
if reflect.ValueOf(ff).CanConvert(dvt) {
setVal(destFieldVal, ff != "" || o.acceptBlank, ff)
} else {
panic(fmt.Errorf("unsupported type %T", v))
}
}
}
return nil
}

View file

@ -14,6 +14,7 @@ import (
"dynatron.me/x/stillbox/pkg/alerting"
"dynatron.me/x/stillbox/pkg/config"
"dynatron.me/x/stillbox/pkg/talkgroups/tgstore"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -115,6 +116,14 @@ var (
TalkgroupGroup: "Wide Area",
TalkgroupLabel: "Wide Area 1 FD/EMS Intercity",
}
Pag1 = tgstore.Pagination{
Pagination: common.Pagination{
Page: common.PtrTo(1),
PerPage: common.PtrTo(2),
},
OrderBy: common.PtrTo(tgstore.TGOrderID),
}
)
func makeRequest(fixture string) *http.Request {
@ -222,6 +231,13 @@ func TestUnmarshal(t *testing.T) {
expect: realSim,
opts: []forms.Option{forms.WithAcceptBlank(), forms.WithParseLocalTime()},
},
{
name: "urlencode pagination",
r: makeRequest("urlenc3.http"),
dest: &tgstore.Pagination{},
expect: &Pag1,
opts: []forms.Option{forms.WithTag("json"), forms.WithAcceptBlank(), forms.WithOmitEmpty()},
},
}
for _, tc := range tests {

View file

@ -13,9 +13,16 @@ func (g GetTalkgroupWithLearnedRow) GetLearned() bool { return g
func (g GetTalkgroupsWithLearnedRow) GetTalkgroup() Talkgroup { return g.Talkgroup }
func (g GetTalkgroupsWithLearnedRow) GetSystem() System { return g.System }
func (g GetTalkgroupsWithLearnedRow) GetLearned() bool { return g.Talkgroup.Learned }
func (g GetTalkgroupsWithLearnedPRow) GetTalkgroup() Talkgroup { return g.Talkgroup }
func (g GetTalkgroupsWithLearnedPRow) GetSystem() System { return g.System }
func (g GetTalkgroupsWithLearnedPRow) 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.Talkgroup.Learned }
func (g GetTalkgroupsWithLearnedBySystemPRow) GetTalkgroup() Talkgroup { return g.Talkgroup }
func (g GetTalkgroupsWithLearnedBySystemPRow) GetSystem() System { return g.System }
func (g GetTalkgroupsWithLearnedBySystemPRow) 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 }

View file

@ -1244,6 +1244,127 @@ func (_c *Store_GetTalkgroupsWithLearnedBySystem_Call) RunAndReturn(run func(con
return _c
}
// GetTalkgroupsWithLearnedBySystemP provides a mock function with given fields: ctx, system, offset, perPage
func (_m *Store) GetTalkgroupsWithLearnedBySystemP(ctx context.Context, system int32, offset int32, perPage int32) ([]database.GetTalkgroupsWithLearnedBySystemPRow, error) {
ret := _m.Called(ctx, system, offset, perPage)
if len(ret) == 0 {
panic("no return value specified for GetTalkgroupsWithLearnedBySystemP")
}
var r0 []database.GetTalkgroupsWithLearnedBySystemPRow
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, int32, int32, int32) ([]database.GetTalkgroupsWithLearnedBySystemPRow, error)); ok {
return rf(ctx, system, offset, perPage)
}
if rf, ok := ret.Get(0).(func(context.Context, int32, int32, int32) []database.GetTalkgroupsWithLearnedBySystemPRow); ok {
r0 = rf(ctx, system, offset, perPage)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]database.GetTalkgroupsWithLearnedBySystemPRow)
}
}
if rf, ok := ret.Get(1).(func(context.Context, int32, int32, int32) error); ok {
r1 = rf(ctx, system, offset, perPage)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Store_GetTalkgroupsWithLearnedBySystemP_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTalkgroupsWithLearnedBySystemP'
type Store_GetTalkgroupsWithLearnedBySystemP_Call struct {
*mock.Call
}
// GetTalkgroupsWithLearnedBySystemP is a helper method to define mock.On call
// - ctx context.Context
// - system int32
// - offset int32
// - perPage int32
func (_e *Store_Expecter) GetTalkgroupsWithLearnedBySystemP(ctx interface{}, system interface{}, offset interface{}, perPage interface{}) *Store_GetTalkgroupsWithLearnedBySystemP_Call {
return &Store_GetTalkgroupsWithLearnedBySystemP_Call{Call: _e.mock.On("GetTalkgroupsWithLearnedBySystemP", ctx, system, offset, perPage)}
}
func (_c *Store_GetTalkgroupsWithLearnedBySystemP_Call) Run(run func(ctx context.Context, system int32, offset int32, perPage int32)) *Store_GetTalkgroupsWithLearnedBySystemP_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(int32), args[2].(int32), args[3].(int32))
})
return _c
}
func (_c *Store_GetTalkgroupsWithLearnedBySystemP_Call) Return(_a0 []database.GetTalkgroupsWithLearnedBySystemPRow, _a1 error) *Store_GetTalkgroupsWithLearnedBySystemP_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *Store_GetTalkgroupsWithLearnedBySystemP_Call) RunAndReturn(run func(context.Context, int32, int32, int32) ([]database.GetTalkgroupsWithLearnedBySystemPRow, error)) *Store_GetTalkgroupsWithLearnedBySystemP_Call {
_c.Call.Return(run)
return _c
}
// GetTalkgroupsWithLearnedP provides a mock function with given fields: ctx, offset, perPage
func (_m *Store) GetTalkgroupsWithLearnedP(ctx context.Context, offset int32, perPage int32) ([]database.GetTalkgroupsWithLearnedPRow, error) {
ret := _m.Called(ctx, offset, perPage)
if len(ret) == 0 {
panic("no return value specified for GetTalkgroupsWithLearnedP")
}
var r0 []database.GetTalkgroupsWithLearnedPRow
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, int32, int32) ([]database.GetTalkgroupsWithLearnedPRow, error)); ok {
return rf(ctx, offset, perPage)
}
if rf, ok := ret.Get(0).(func(context.Context, int32, int32) []database.GetTalkgroupsWithLearnedPRow); ok {
r0 = rf(ctx, offset, perPage)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]database.GetTalkgroupsWithLearnedPRow)
}
}
if rf, ok := ret.Get(1).(func(context.Context, int32, int32) error); ok {
r1 = rf(ctx, offset, perPage)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Store_GetTalkgroupsWithLearnedP_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTalkgroupsWithLearnedP'
type Store_GetTalkgroupsWithLearnedP_Call struct {
*mock.Call
}
// GetTalkgroupsWithLearnedP is a helper method to define mock.On call
// - ctx context.Context
// - offset int32
// - perPage int32
func (_e *Store_Expecter) GetTalkgroupsWithLearnedP(ctx interface{}, offset interface{}, perPage interface{}) *Store_GetTalkgroupsWithLearnedP_Call {
return &Store_GetTalkgroupsWithLearnedP_Call{Call: _e.mock.On("GetTalkgroupsWithLearnedP", ctx, offset, perPage)}
}
func (_c *Store_GetTalkgroupsWithLearnedP_Call) Run(run func(ctx context.Context, offset int32, perPage int32)) *Store_GetTalkgroupsWithLearnedP_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(int32), args[2].(int32))
})
return _c
}
func (_c *Store_GetTalkgroupsWithLearnedP_Call) Return(_a0 []database.GetTalkgroupsWithLearnedPRow, _a1 error) *Store_GetTalkgroupsWithLearnedP_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *Store_GetTalkgroupsWithLearnedP_Call) RunAndReturn(run func(context.Context, int32, int32) ([]database.GetTalkgroupsWithLearnedPRow, error)) *Store_GetTalkgroupsWithLearnedP_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 int) (database.User, error) {
ret := _m.Called(ctx, id)

View file

@ -30,6 +30,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)
GetTalkgroupsWithLearnedBySystemP(ctx context.Context, system int32, offset int32, perPage int32) ([]GetTalkgroupsWithLearnedBySystemPRow, error)
GetTalkgroupsWithLearnedP(ctx context.Context, offset int32, perPage int32) ([]GetTalkgroupsWithLearnedPRow, 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)

View file

@ -377,6 +377,112 @@ func (q *Queries) GetTalkgroupsWithLearnedBySystem(ctx context.Context, system i
return items, nil
}
const getTalkgroupsWithLearnedBySystemP = `-- name: GetTalkgroupsWithLearnedBySystemP :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, tg.learned, tg.ignored, sys.id, sys.name
FROM talkgroups tg
JOIN systems sys ON tg.system_id = sys.id
WHERE tg.system_id = $1
ORDER BY tg.system_id ASC, tg.tgid ASC
OFFSET $2 ROWS
FETCH NEXT $3 ROWS ONLY
`
type GetTalkgroupsWithLearnedBySystemPRow struct {
Talkgroup Talkgroup `json:"talkgroup"`
System System `json:"system"`
}
func (q *Queries) GetTalkgroupsWithLearnedBySystemP(ctx context.Context, system int32, offset int32, perPage int32) ([]GetTalkgroupsWithLearnedBySystemPRow, error) {
rows, err := q.db.Query(ctx, getTalkgroupsWithLearnedBySystemP, system, offset, perPage)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetTalkgroupsWithLearnedBySystemPRow
for rows.Next() {
var i GetTalkgroupsWithLearnedBySystemPRow
if err := rows.Scan(
&i.Talkgroup.ID,
&i.Talkgroup.SystemID,
&i.Talkgroup.TGID,
&i.Talkgroup.Name,
&i.Talkgroup.AlphaTag,
&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.Talkgroup.Ignored,
&i.System.ID,
&i.System.Name,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getTalkgroupsWithLearnedP = `-- name: GetTalkgroupsWithLearnedP :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, tg.learned, tg.ignored, sys.id, sys.name
FROM talkgroups tg
JOIN systems sys ON tg.system_id = sys.id
WHERE ignored IS NOT TRUE
ORDER BY tg.system_id ASC, tg.tgid ASC
OFFSET $1 ROWS
FETCH NEXT $2 ROWS ONLY
`
type GetTalkgroupsWithLearnedPRow struct {
Talkgroup Talkgroup `json:"talkgroup"`
System System `json:"system"`
}
func (q *Queries) GetTalkgroupsWithLearnedP(ctx context.Context, offset int32, perPage int32) ([]GetTalkgroupsWithLearnedPRow, error) {
rows, err := q.db.Query(ctx, getTalkgroupsWithLearnedP, offset, perPage)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetTalkgroupsWithLearnedPRow
for rows.Next() {
var i GetTalkgroupsWithLearnedPRow
if err := rows.Scan(
&i.Talkgroup.ID,
&i.Talkgroup.SystemID,
&i.Talkgroup.TGID,
&i.Talkgroup.Name,
&i.Talkgroup.AlphaTag,
&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.Talkgroup.Ignored,
&i.System.ID,
&i.System.Name,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const restoreTalkgroupVersion = `-- name: RestoreTalkgroupVersion :one
INSERT INTO talkgroups(
system_id,

View file

@ -59,6 +59,14 @@ func badRequest(err error) render.Renderer {
}
}
func badRequestErrText(err error) render.Renderer {
return &errResponse{
Err: err,
Code: http.StatusBadRequest,
Error: "Bad request: " + err.Error(),
}
}
func recordNotFound(err error) render.Renderer {
return &errResponse{
Err: err,
@ -67,7 +75,7 @@ func recordNotFound(err error) render.Renderer {
}
}
func errTextNotFound(err error) render.Renderer {
func notFoundErrText(err error) render.Renderer {
return &errResponse{
Err: err,
Code: http.StatusNotFound,
@ -86,8 +94,9 @@ func internalError(err error) render.Renderer {
type errResponder func(error) render.Renderer
var statusMapping = map[error]errResponder{
tgstore.ErrNoSuchSystem: errTextNotFound,
tgstore.ErrNotFound: errTextNotFound,
tgstore.ErrNoSuchSystem: notFoundErrText,
tgstore.ErrNotFound: notFoundErrText,
tgstore.ErrInvalidOrderBy: badRequestErrText,
pgx.ErrNoRows: recordNotFound,
}

View file

@ -12,6 +12,8 @@ import (
"github.com/go-chi/chi/v5"
)
const DefaultPerPage = 20
type talkgroupAPI struct {
}
@ -19,10 +21,15 @@ func (tga *talkgroupAPI) Subrouter() http.Handler {
r := chi.NewMux()
r.Get(`/{system:\d+}/{id:\d+}`, tga.get)
r.Put(`/{system:\d+}/{id:\d+}`, tga.put)
r.Put(`/{system:\d+}`, tga.putTalkgroups)
r.Get(`/{system:\d+}/`, tga.get)
r.Get("/", tga.get)
r.Put(`/{system:\d+}/{id:\d+}`, tga.put)
r.Put(`/{system:\d+}`, tga.putTalkgroups)
r.Post(`/{system:\d+}/`, tga.postPaginated)
r.Post(`/`, tga.postPaginated)
r.Post("/import", tga.tgImport)
return r
@ -83,6 +90,42 @@ func (tga *talkgroupAPI) get(w http.ResponseWriter, r *http.Request) {
respond(w, r, res)
}
func (tga *talkgroupAPI) postPaginated(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
tgs := tgstore.FromCtx(ctx)
var p tgParams
err := decodeParams(&p, r)
if err != nil {
wErr(w, r, badRequest(err))
return
}
input := &tgstore.Pagination{}
err = forms.Unmarshal(r, input, forms.WithTag("json"), forms.WithAcceptBlank(), forms.WithOmitEmpty())
if err != nil {
wErr(w, r, badRequest(err))
return
}
var res interface{}
switch {
case p.System != nil:
res, err = tgs.SystemTGs(ctx, int32(*p.System), tgstore.WithPagination(input, DefaultPerPage))
default:
// get all talkgroups
res, err = tgs.TGs(ctx, nil, tgstore.WithPagination(input, DefaultPerPage))
}
if err != nil {
wErr(w, r, autoError(err))
return
}
respond(w, r, res)
}
func (tga *talkgroupAPI) put(w http.ResponseWriter, r *http.Request) {
var id tgParams
err := decodeParams(&id, r)

View file

@ -73,9 +73,9 @@ func (s *DatabaseSink) toAddCallParams(call *calls.Call) database.AddCallParams
System: call.System,
Talkgroup: call.Talkgroup,
CallDate: pgtype.Timestamptz{Time: call.DateTime, Valid: true},
AudioName: common.PtrOrNull(call.AudioName),
AudioName: common.NilIfZero(call.AudioName),
AudioBlob: call.Audio,
AudioType: common.PtrOrNull(call.AudioType),
AudioType: common.NilIfZero(call.AudioType),
Duration: call.Duration.MsInt32Ptr(),
Frequency: call.Frequency,
Frequencies: call.Frequencies,

View file

@ -83,9 +83,9 @@ func (car *CallUploadRequest) ToCall(submitter auth.UserID) (*calls.Call, error)
Frequency: car.Frequency,
Frequencies: car.Frequencies,
Patches: car.Patches,
TalkgroupLabel: common.PtrOrNull(car.TalkgroupLabel),
TGAlphaTag: common.PtrOrNull(car.TalkgroupTag),
TalkgroupGroup: common.PtrOrNull(car.TalkgroupGroup),
TalkgroupLabel: common.NilIfZero(car.TalkgroupLabel),
TGAlphaTag: common.NilIfZero(car.TalkgroupTag),
TalkgroupGroup: common.NilIfZero(car.TalkgroupGroup),
Source: car.Source,
}, !car.DontStore)
}

View file

@ -23,6 +23,7 @@ type tgMap map[tgsp.ID]*tgsp.Talkgroup
var (
ErrNotFound = errors.New("talkgroup not found")
ErrNoSuchSystem = errors.New("no such system")
ErrInvalidOrderBy = errors.New("invalid pagination orderBy value")
)
type Store interface {
@ -36,13 +37,13 @@ type Store interface {
TG(ctx context.Context, tg tgsp.ID) (*tgsp.Talkgroup, error)
// TGs retrieves many talkgroups from the Store.
TGs(ctx context.Context, tgs tgsp.IDs) ([]*tgsp.Talkgroup, error)
TGs(ctx context.Context, tgs tgsp.IDs, opts ...option) ([]*tgsp.Talkgroup, error)
// LearnTG learns the talkgroup from a Call.
LearnTG(ctx context.Context, call *calls.Call) (*tgsp.Talkgroup, error)
// SystemTGs retrieves all Talkgroups associated with a System.
SystemTGs(ctx context.Context, systemID int32) ([]*tgsp.Talkgroup, error)
SystemTGs(ctx context.Context, systemID int32, opts ...option) ([]*tgsp.Talkgroup, error)
// SystemName retrieves a system name from the store. It returns the record and whether one was found.
SystemName(ctx context.Context, id int) (string, bool)
@ -63,6 +64,55 @@ type Store interface {
HUP(*config.Config)
}
type options struct {
pagination *Pagination
perPageDefault int
}
func sOpt(opts []option) (o options) {
for _, opt := range opts {
opt(&o)
}
return
}
type option func(*options)
func WithPagination(p *Pagination, defPerPage int) option {
return func(o *options) {
o.pagination = p
o.perPageDefault = defPerPage
}
}
type TGOrder string
const (
TGOrderTGID TGOrder = "tgid"
TGOrderGroup TGOrder = "group"
TGOrderName TGOrder = "name"
TGOrderID TGOrder = "id"
)
func (t *TGOrder) IsValid() bool {
if t == nil {
return true
}
switch *t {
case TGOrderTGID, TGOrderGroup, TGOrderName, TGOrderID:
return true
}
return false
}
type Pagination struct {
common.Pagination
OrderBy *TGOrder `json:"orderBy"`
}
type storeCtxKey string
const StoreCtxKey storeCtxKey = "store"
@ -148,20 +198,29 @@ func (t *cache) add(rec *tgsp.Talkgroup) {
t.Lock()
defer t.Unlock()
t.addNoLock(rec)
}
func (t *cache) addNoLock(rec *tgsp.Talkgroup) {
tg := tgsp.TG(rec.System.ID, rec.Talkgroup.TGID)
t.tgs[tg] = rec
t.systems[int32(rec.System.ID)] = rec.System.Name
}
type row interface {
type rowType interface {
database.GetTalkgroupsRow | database.GetTalkgroupsWithLearnedRow |
database.GetTalkgroupsWithLearnedBySystemRow | database.GetTalkgroupWithLearnedRow
database.GetTalkgroupsWithLearnedBySystemRow | database.GetTalkgroupWithLearnedRow |
database.GetTalkgroupsWithLearnedBySystemPRow | database.GetTalkgroupsWithLearnedPRow
row
}
type row interface {
GetTalkgroup() database.Talkgroup
GetSystem() database.System
GetLearned() bool
}
func rowToTalkgroup[T row](r T) *tgsp.Talkgroup {
func rowToTalkgroup[T rowType](r T) *tgsp.Talkgroup {
return &tgsp.Talkgroup{
Talkgroup: r.GetTalkgroup(),
System: r.GetSystem(),
@ -169,10 +228,13 @@ func rowToTalkgroup[T row](r T) *tgsp.Talkgroup {
}
}
func addToRowList[T row](t *cache, r []*tgsp.Talkgroup, tgRecords []T) []*tgsp.Talkgroup {
func addToRowListS[T rowType](t *cache, r []*tgsp.Talkgroup, tgRecords []T) []*tgsp.Talkgroup {
t.Lock()
defer t.Unlock()
for _, rec := range tgRecords {
tg := rowToTalkgroup(rec)
t.add(tg)
t.addNoLock(tg)
r = append(r, tg)
}
@ -180,8 +242,25 @@ func addToRowList[T row](t *cache, r []*tgsp.Talkgroup, tgRecords []T) []*tgsp.T
return r
}
func (t *cache) TGs(ctx context.Context, tgs tgsp.IDs) ([]*tgsp.Talkgroup, error) {
func addToRowList[T rowType](t *cache, tgRecords []T) []*tgsp.Talkgroup {
t.Lock()
defer t.Unlock()
r := make([]*tgsp.Talkgroup, 0, len(tgRecords))
for _, rec := range tgRecords {
tg := rowToTalkgroup(rec)
t.addNoLock(tg)
r = append(r, tg)
}
return r
}
func (t *cache) TGs(ctx context.Context, tgs tgsp.IDs, opts ...option) ([]*tgsp.Talkgroup, error) {
db := database.FromCtx(ctx)
r := make([]*tgsp.Talkgroup, 0, len(tgs))
opt := sOpt(opts)
var err error
if tgs != nil {
toGet := make(tgsp.IDs, 0, len(tgs))
@ -194,20 +273,30 @@ func (t *cache) TGs(ctx context.Context, tgs tgsp.IDs) ([]*tgsp.Talkgroup, error
}
}
tgRecords, err := database.FromCtx(ctx).GetTalkgroupsWithLearnedBySysTGID(ctx, toGet.Tuples())
tgRecords, err := db.GetTalkgroupsWithLearnedBySysTGID(ctx, toGet.Tuples())
if err != nil {
return nil, err
}
return addToRowList(t, r, tgRecords), nil
return addToRowList(t, tgRecords), nil
}
// get all talkgroups
tgRecords, err := database.FromCtx(ctx).GetTalkgroupsWithLearned(ctx)
if opt.pagination != nil {
offset, perPage := opt.pagination.OffsetPerPage(opt.perPageDefault)
tgRecords, err := db.GetTalkgroupsWithLearnedP(ctx, offset, perPage)
if err != nil {
return nil, err
}
return addToRowList(t, r, tgRecords), nil
return addToRowListS(t, r, tgRecords), nil
}
tgRecords, err := db.GetTalkgroupsWithLearned(ctx)
if err != nil {
return nil, err
}
return addToRowListS(t, r, tgRecords), nil
}
func (t *cache) Load(ctx context.Context, tgs database.TGTuples) error {
@ -236,14 +325,25 @@ func (t *cache) Weight(ctx context.Context, id tgsp.ID, tm time.Time) float64 {
return float64(m)
}
func (t *cache) SystemTGs(ctx context.Context, systemID int32) ([]*tgsp.Talkgroup, error) {
recs, err := database.FromCtx(ctx).GetTalkgroupsWithLearnedBySystem(ctx, systemID)
func (t *cache) SystemTGs(ctx context.Context, systemID int32, opts ...option) ([]*tgsp.Talkgroup, error) {
db := database.FromCtx(ctx)
opt := sOpt(opts)
var err error
if opt.pagination != nil {
offset, perPage := opt.pagination.OffsetPerPage(opt.perPageDefault)
recs, err := db.GetTalkgroupsWithLearnedBySystemP(ctx, systemID, offset, perPage)
if err != nil {
return nil, err
}
return addToRowList(t, recs), nil
}
recs, err := db.GetTalkgroupsWithLearnedBySystem(ctx, systemID)
if err != nil {
return nil, err
}
r := make([]*tgsp.Talkgroup, 0, len(recs))
return addToRowList(t, r, recs), nil
return addToRowList(t, recs), nil
}
func (t *cache) TG(ctx context.Context, tg tgsp.ID) (*tgsp.Talkgroup, error) {

View file

@ -30,8 +30,8 @@ type Alias struct {
func tgToAlias(tg *talkgroups.Talkgroup) Alias {
return Alias{
XMLName: xml.Name{Local: "alias"},
Name: common.ZeroOr(tg.Name),
Group: common.ZeroOr(tg.TGGroup),
Name: common.ZeroIfNil(tg.Name),
Group: common.ZeroIfNil(tg.TGGroup),
List: "Stillbox",
IDs: []ID{
ID{

View file

@ -31,6 +31,17 @@ FROM talkgroups tg
JOIN systems sys ON tg.system_id = sys.id
WHERE (tg.system_id, tg.tgid) = (@system_id, @tgid);
-- name: GetTalkgroupsWithLearnedBySystemP :many
SELECT
sqlc.embed(tg), sqlc.embed(sys)
FROM talkgroups tg
JOIN systems sys ON tg.system_id = sys.id
WHERE tg.system_id = @system
ORDER BY tg.system_id ASC, tg.tgid ASC
OFFSET sqlc.arg('offset') ROWS
FETCH NEXT sqlc.arg('per_page') ROWS ONLY;
;
-- name: GetTalkgroupsWithLearnedBySystem :many
SELECT
sqlc.embed(tg), sqlc.embed(sys)
@ -45,6 +56,16 @@ FROM talkgroups tg
JOIN systems sys ON tg.system_id = sys.id
WHERE ignored IS NOT TRUE;
-- name: GetTalkgroupsWithLearnedP :many
SELECT
sqlc.embed(tg), sqlc.embed(sys)
FROM talkgroups tg
JOIN systems sys ON tg.system_id = sys.id
WHERE ignored IS NOT TRUE
ORDER BY tg.system_id ASC, tg.tgid ASC
OFFSET sqlc.arg('offset') ROWS
FETCH NEXT sqlc.arg('per_page') ROWS ONLY;
-- name: GetSystemName :one
SELECT name FROM systems WHERE id = @system_id;