[counter] Allow counterTopList to specify how to sort

Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
Knut Ahlers 2024-04-04 19:04:06 +02:00
parent 94b040ed81
commit 293a7d9e30
Signed by: luzifer
SSH key fingerprint: SHA256:/xtE5lCgiRDQr8SLxHMS92ZBlACmATUmF1crK16Ks4E
4 changed files with 69 additions and 10 deletions

View file

@ -128,9 +128,9 @@ Example:
### `counterTopList` ### `counterTopList`
Returns the top n counters for the given prefix as objects with Name and Value fields Returns the top n counters for the given prefix as objects with Name and Value fields. Can be ordered by `name` / `value` / `first_seen` / `last_modified` ascending (`ASC`) or descending (`DESC`): i.e. `last_modified DESC` defaults to `value DESC`
Syntax: `counterTopList <prefix> <n>` Syntax: `counterTopList <prefix> <n> [orderBy]`
Example: Example:
@ -467,7 +467,7 @@ Example:
``` ```
# Your int this hour: {{ printf "%.0f" (mulf (seededRandom (list "int" .username (now | date "2006-01-02 15") | join ":")) 100) }}% # Your int this hour: {{ printf "%.0f" (mulf (seededRandom (list "int" .username (now | date "2006-01-02 15") | join ":")) 100) }}%
< Your int this hour: 70% < Your int this hour: 88%
``` ```
### `spotifyCurrentPlaying` ### `spotifyCurrentPlaying`

View file

@ -166,11 +166,11 @@ func Register(args plugins.RegistrationArguments) (err error) {
}, },
}) })
args.RegisterTemplateFunction("counterTopList", plugins.GenericTemplateFunctionGetter(func(prefix string, n int) ([]counter, error) { args.RegisterTemplateFunction("counterTopList", plugins.GenericTemplateFunctionGetter(func(prefix string, n int, orderBy string) ([]counter, error) {
return getCounterTopList(db, prefix, n) return getCounterTopList(db, prefix, n, orderBy)
}), plugins.TemplateFuncDocumentation{ }), plugins.TemplateFuncDocumentation{
Description: "Returns the top n counters for the given prefix as objects with Name and Value fields", Description: "Returns the top n counters for the given prefix as objects with Name and Value fields. Can be ordered by `name` / `value` / `first_seen` / `last_modified` ascending (`ASC`) or descending (`DESC`): i.e. `last_modified DESC` defaults to `value DESC`",
Syntax: `counterTopList <prefix> <n>`, Syntax: `counterTopList <prefix> <n> [orderBy]`,
Example: &plugins.TemplateFuncDocumentationExample{ Example: &plugins.TemplateFuncDocumentationExample{
Template: `{{ range (counterTopList (list .channel "test" "" | join ":") 3) }}{{ .Name }}: {{ .Value }} - {{ end }}`, Template: `{{ range (counterTopList (list .channel "test" "" | join ":") 3) }}{{ .Name }}: {{ .Value }} - {{ end }}`,
FakedOutput: "#example:test:foo: 5 - #example:test:bar: 4 - ", FakedOutput: "#example:test:foo: 5 - #example:test:bar: 4 - ",

View file

@ -1,12 +1,15 @@
package counter package counter
import ( import (
"fmt"
"strings"
"time" "time"
"github.com/pkg/errors" "github.com/pkg/errors"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/clause" "gorm.io/gorm/clause"
"github.com/Luzifer/go_helpers/v2/str"
"github.com/Luzifer/twitch-bot/v3/internal/helpers" "github.com/Luzifer/twitch-bot/v3/internal/helpers"
"github.com/Luzifer/twitch-bot/v3/pkg/database" "github.com/Luzifer/twitch-bot/v3/pkg/database"
) )
@ -79,12 +82,39 @@ func getCounterRank(db database.Connector, prefix, name string) (rank, count int
return rank, count, nil return rank, count, nil
} }
func getCounterTopList(db database.Connector, prefix string, n int) ([]counter, error) { func getCounterTopList(db database.Connector, prefix string, n int, orderBy ...string) ([]counter, error) {
var cc []counter var (
cc []counter
order string
validOrderCols = []string{"first_seen", "last_modified", "name", "value"}
validOrderDirs = []string{"ASC", "DESC"}
)
if len(orderBy) == 0 || orderBy[0] == "" {
order = "value DESC"
} else {
order = orderBy[0]
}
col, dir, _ := strings.Cut(order, " ")
if col == "" {
col = "value"
}
if dir == "" {
dir = "ASC"
}
if !str.StringInSlice(col, validOrderCols) {
return nil, fmt.Errorf("invalid orderBy column")
}
if !str.StringInSlice(dir, validOrderDirs) {
return nil, fmt.Errorf("invalid orderBy direction")
}
err := helpers.Retry(func() error { err := helpers.Retry(func() error {
return db.DB(). return db.DB().
Order("value DESC"). Order(strings.Join([]string{col, dir}, " ")).
Limit(n). Limit(n).
Find(&cc, "name LIKE ?", prefix+"%"). Find(&cc, "name LIKE ?", prefix+"%").
Error Error

View file

@ -64,6 +64,35 @@ func TestCounterTopListAndRank(t *testing.T) {
{Name: "#example:test:3", Value: 3, FirstSeen: testTime, LastModified: testTime}, {Name: "#example:test:3", Value: 3, FirstSeen: testTime, LastModified: testTime},
}, cc) }, cc)
cc, err = getCounterTopList(dbc, fmt.Sprintf(counterTemplate, ""), 3, "name DESC")
require.NoError(t, err)
assert.Len(t, cc, 3)
assert.Equal(t, []counter{
{Name: "#example:test:5", Value: 5, FirstSeen: testTime, LastModified: testTime},
{Name: "#example:test:4", Value: 4, FirstSeen: testTime, LastModified: testTime},
{Name: "#example:test:3", Value: 3, FirstSeen: testTime, LastModified: testTime},
}, cc)
cc, err = getCounterTopList(dbc, fmt.Sprintf(counterTemplate, ""), 3, "name")
require.NoError(t, err)
assert.Len(t, cc, 3)
assert.Equal(t, []counter{
{Name: "#example:test:0", Value: 0, FirstSeen: testTime, LastModified: testTime},
{Name: "#example:test:1", Value: 1, FirstSeen: testTime, LastModified: testTime},
{Name: "#example:test:2", Value: 2, FirstSeen: testTime, LastModified: testTime},
}, cc)
_, err = getCounterTopList(dbc, fmt.Sprintf(counterTemplate, ""), 3, "foobar")
assert.Error(t, err)
_, err = getCounterTopList(dbc, fmt.Sprintf(counterTemplate, ""), 3, "name foo")
assert.Error(t, err)
_, err = getCounterTopList(dbc, fmt.Sprintf(counterTemplate, ""), 3, "name ASC; DROP TABLE counters;")
assert.Error(t, err)
rank, count, err := getCounterRank(dbc, rank, count, err := getCounterRank(dbc,
fmt.Sprintf(counterTemplate, ""), fmt.Sprintf(counterTemplate, ""),
fmt.Sprintf(counterTemplate, 4)) fmt.Sprintf(counterTemplate, 4))