Overhaul filters

This commit is contained in:
rxdn 2021-08-16 21:02:47 +01:00
parent 2f2111d576
commit 2b355f42eb
4 changed files with 125 additions and 226 deletions

View File

@ -1,30 +1,14 @@
package api
import (
"errors"
"github.com/TicketsBot/GoPanel/botcontext"
dbclient "github.com/TicketsBot/GoPanel/database"
"github.com/TicketsBot/GoPanel/rpc/cache"
"github.com/TicketsBot/GoPanel/utils"
"github.com/TicketsBot/database"
"github.com/gin-gonic/gin"
"math"
"net/http"
"strconv"
)
const (
pageLimit = 30
)
type filterType uint8
const (
filterTypeNone filterType = iota
filterTypeTicketId
filterTypeUsername
filterTypeUserId
)
const pageLimit = 15
type transcript struct {
TicketId int `json:"ticket_id"`
@ -36,6 +20,24 @@ type transcript struct {
func ListTranscripts(ctx *gin.Context) {
guildId := ctx.Keys["guildid"].(uint64)
var queryOptions wrappedQueryOptions
if err := ctx.BindJSON(&queryOptions); err != nil {
ctx.JSON(400, utils.ErrorJson(err))
return
}
opts, err := queryOptions.toQueryOptions(guildId)
if err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
tickets, err := dbclient.Client.Tickets.GetByOptions(opts)
if err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
botContext, err := botcontext.ContextForGuild(guildId)
if err != nil {
ctx.AbortWithStatusJSON(500, gin.H{
@ -45,30 +47,6 @@ func ListTranscripts(ctx *gin.Context) {
return
}
// db functions will handle if 0
before, _ := strconv.Atoi(ctx.Query("before"))
after, _ := strconv.Atoi(ctx.Query("after"))
var tickets []database.TicketWithCloseReason
var status int
filterType := getFilterType(ctx)
switch filterType {
case filterTypeNone:
tickets, status, err = getTickets(guildId, before, after)
case filterTypeTicketId:
tickets, status, err = getTicketsByTicketId(guildId, ctx)
case filterTypeUsername:
tickets, status, err = getTicketsByUsername(guildId, before, after, ctx)
case filterTypeUserId:
tickets, status, err = getTicketsByUserId(guildId, before, after, ctx)
}
if err != nil {
ctx.JSON(status, utils.ErrorJson(err))
return
}
// Create a mapping user_id -> username so we can skip duplicates
usernames := make(map[uint64]string)
for _, ticket := range tickets {
@ -102,164 +80,30 @@ func ListTranscripts(ctx *gin.Context) {
return
}
transcripts := make([]transcript, len(tickets))
for i, ticket := range tickets {
var rating *uint8
if v, ok := ratings[ticket.Id]; ok {
rating = &v
// Get close reasons
closeReasons, err := dbclient.Client.CloseReason.GetMulti(guildId, ticketIds)
if err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
transcripts[i] = transcript{
transcripts := make([]transcript, len(tickets))
for i, ticket := range tickets {
transcript := transcript{
TicketId: ticket.Id,
Username: usernames[ticket.UserId],
CloseReason: ticket.CloseReason,
Rating: rating,
}
if v, ok := ratings[ticket.Id]; ok {
transcript.Rating = &v
}
if v, ok := closeReasons[ticket.Id]; ok {
transcript.CloseReason = &v
}
transcripts[i] = transcript
}
ctx.JSON(200, transcripts)
}
func getFilterType(ctx *gin.Context) filterType {
if ctx.Query("ticketid") != "" {
return filterTypeTicketId
} else if ctx.Query("username") != "" {
return filterTypeUsername
} else if ctx.Query("userid") != "" {
return filterTypeUserId
} else {
return filterTypeNone
}
}
func getTickets(guildId uint64, before, after int) ([]database.TicketWithCloseReason, int, error) {
var tickets []database.TicketWithCloseReason
var err error
if before <= 0 && after <= 0 {
tickets, err = dbclient.Client.Tickets.GetGuildClosedTicketsBeforeWithCloseReason(guildId, pageLimit, math.MaxInt32)
} else if before > 0 {
tickets, err = dbclient.Client.Tickets.GetGuildClosedTicketsBeforeWithCloseReason(guildId, pageLimit, before)
} else { // after > 0
// returns in ascending order, must reverse
tickets, err = dbclient.Client.Tickets.GetGuildClosedTicketsAfterWithCloseReason(guildId, pageLimit, after)
if err == nil {
reverse(tickets)
}
}
status := http.StatusOK
if err != nil {
status = http.StatusInternalServerError
}
return tickets, status, err
}
// (tickets, statusCode, error)
func getTicketsByTicketId(guildId uint64, ctx *gin.Context) ([]database.TicketWithCloseReason, int, error) {
ticketId, err := strconv.Atoi(ctx.Query("ticketid"))
if err != nil {
return nil, 400, err
}
ticket, err := dbclient.Client.Tickets.Get(ticketId, guildId)
if err != nil {
return nil, http.StatusInternalServerError, err
}
if ticket.Id == 0 {
return nil, http.StatusNotFound, errors.New("ticket not found")
}
closeReason, ok, err := dbclient.Client.CloseReason.Get(guildId, ticketId)
if err != nil {
return nil, http.StatusInternalServerError, err
}
data := database.TicketWithCloseReason{
Ticket: ticket,
}
if ok {
data.CloseReason = &closeReason
}
return []database.TicketWithCloseReason{data}, http.StatusOK, nil
}
// (tickets, statusCode, error)
func getTicketsByUsername(guildId uint64, before, after int, ctx *gin.Context) ([]database.TicketWithCloseReason, int, error) {
username := ctx.Query("username")
botContext, err := botcontext.ContextForGuild(guildId)
if err != nil {
return nil, http.StatusInternalServerError, err
}
members, err := botContext.SearchMembers(guildId, username)
if err != nil {
return nil, http.StatusInternalServerError, err
}
userIds := make([]uint64, len(members)) // capped at 100
for i, member := range members {
userIds[i] = member.User.Id
}
var tickets []database.TicketWithCloseReason
if before <= 0 && after <= 0 {
tickets, err = dbclient.Client.Tickets.GetClosedByAnyBeforeWithCloseReason(guildId, userIds, math.MaxInt32, pageLimit)
} else if before > 0 {
tickets, err = dbclient.Client.Tickets.GetClosedByAnyBeforeWithCloseReason(guildId, userIds, before, pageLimit)
} else { // after > 0
// returns in ascending order, must reverse
tickets, err = dbclient.Client.Tickets.GetClosedByAnyAfterWithCloseReason(guildId, userIds, after, pageLimit)
if err == nil {
reverse(tickets)
}
}
if err != nil {
return nil, http.StatusInternalServerError, err
}
return tickets, http.StatusOK, nil
}
// (tickets, statusCode, error)
func getTicketsByUserId(guildId uint64, before, after int, ctx *gin.Context) ([]database.TicketWithCloseReason, int, error) {
userId, err := strconv.ParseUint(ctx.Query("userid"), 10, 64)
if err != nil {
return nil, 400, err
}
var tickets []database.TicketWithCloseReason
if before <= 0 && after <= 0 {
tickets, err = dbclient.Client.Tickets.GetClosedByAnyBeforeWithCloseReason(guildId, []uint64{userId}, math.MaxInt32, pageLimit)
} else if before > 0 {
tickets, err = dbclient.Client.Tickets.GetClosedByAnyBeforeWithCloseReason(guildId, []uint64{userId}, before, pageLimit)
} else { // after > 0
// returns in ascending order, must reverse
tickets, err = dbclient.Client.Tickets.GetClosedByAnyAfterWithCloseReason(guildId, []uint64{userId}, after, pageLimit)
if err == nil {
reverse(tickets)
}
}
if err != nil {
return nil, http.StatusInternalServerError, err
}
return tickets, http.StatusOK, nil
}
func reverse(slice []database.TicketWithCloseReason) {
if len(slice) == 0 {
return
}
for i, j := 0, len(slice)-1; i < j; i, j = i+1, j-1 {
slice[i], slice[j] = slice[j], slice[i]
}
}

View File

@ -0,0 +1,74 @@
package api
import (
"errors"
"github.com/TicketsBot/GoPanel/botcontext"
"github.com/TicketsBot/database"
"github.com/rxdn/gdl/utils"
)
type wrappedQueryOptions struct {
Id int `json:"id,string"`
Username string `json:"username"`
UserId uint64 `json:"user_id,string"`
Page int `json:"page"`
}
func (o *wrappedQueryOptions) toQueryOptions(guildId uint64) (database.TicketQueryOptions, error) {
var userIds []uint64
if len(o.Username) > 0 {
var err error
userIds, err = usernameToIds(guildId, o.Username)
if err != nil {
return database.TicketQueryOptions{}, err
}
// TODO: Do this better
if len(userIds) == 0 {
return database.TicketQueryOptions{}, errors.New("User not found")
}
}
if o.UserId != 0 {
userIds = append(userIds, o.UserId)
}
var offset int
if o.Page > 1 {
offset = pageLimit * (o.Page - 1)
}
opts := database.TicketQueryOptions{
Id: o.Id,
GuildId: guildId,
UserIds: userIds,
Open: utils.BoolPtr(false),
Order: database.OrderTypeDescending,
Limit: pageLimit,
Offset: offset,
}
return opts, nil
}
func usernameToIds(guildId uint64, username string) ([]uint64, error) {
if len(username) > 32 {
return nil, errors.New("username too long")
}
botContext, err := botcontext.ContextForGuild(guildId)
if err != nil {
return nil, err
}
members, err := botContext.SearchMembers(guildId, username)
if err != nil {
return nil, err
}
userIds := make([]uint64, len(members)) // capped at 100
for i, member := range members {
userIds[i] = member.User.Id
}
return userIds, nil
}

View File

@ -99,7 +99,7 @@
let filterSettings = {};
let transcripts = [];
const pageLimit = 30;
const pageLimit = 15;
let page = 1;
let handleInputTicketId = () => {
@ -122,10 +122,8 @@
return;
}
let paginationSettings = {
after: transcripts[0].ticket_id,
};
let paginationSettings = buildPaginationSettings(page - 1);
if (await loadData(paginationSettings)) {
page--;
}
@ -136,42 +134,25 @@
return;
}
let paginationSettings = {
before: transcripts[transcripts.length - 1].ticket_id,
};
let paginationSettings = buildPaginationSettings(page + 1);
if (await loadData(paginationSettings)) {
page++;
}
}
function buildQuery(paginationSettings) {
let query = new URLSearchParams();
if (paginationSettings['before'] !== undefined) {
query.append('before', paginationSettings['before']);
}
if (paginationSettings['after'] !== undefined) {
query.append('after', paginationSettings['after']);
}
if (filterSettings['ticketId'] !== undefined) {
query.append('ticketid', filterSettings.ticketId);
}
if (filterSettings['username'] !== undefined) {
query.append('username', filterSettings.username);
}
if (filterSettings['userId'] !== undefined) {
query.append('userid', filterSettings.userId);
}
return query;
function buildPaginationSettings(page) {
// Undefined fields won't be included in the JSON
return {
id: filterSettings.ticketId,
username: filterSettings.username,
user_id: filterSettings.userId,
page: page,
};
}
async function filter() {
await loadData({});
let opts = buildPaginationSettings(1);
await loadData(opts);
page = 1;
}

2
go.mod
View File

@ -6,7 +6,7 @@ require (
github.com/BurntSushi/toml v0.3.1
github.com/TicketsBot/archiverclient v0.0.0-20210220155137-a562b2f1bbbb
github.com/TicketsBot/common v0.0.0-20210727134627-35eb7ed03a44
github.com/TicketsBot/database v0.0.0-20210809170854-748ae1fff443
github.com/TicketsBot/database v0.0.0-20210816195201-90c765ca95c8
github.com/TicketsBot/worker v0.0.0-20210727130432-3df3cd1246a3
github.com/apex/log v1.1.2
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect