mirror of
https://github.com/superseriousbusiness/gotosocial
synced 2024-11-10 06:54:16 +00:00
[chore] make csv export ordering determinate (#3318)
This commit is contained in:
parent
2f56455eed
commit
f819229988
2 changed files with 75 additions and 66 deletions
|
@ -161,8 +161,8 @@ func (suite *ExportsTestSuite) TestExports() {
|
|||
user: suite.testUsers["local_account_1"],
|
||||
account: suite.testAccounts["local_account_1"],
|
||||
expect: `Account address,Show boosts,Notify on new posts,Languages
|
||||
admin@localhost:8080,true,false,
|
||||
1happyturtle@localhost:8080,true,false,
|
||||
admin@localhost:8080,true,false,
|
||||
`,
|
||||
},
|
||||
// Export Followers.
|
||||
|
|
|
@ -18,12 +18,13 @@
|
|||
package typeutils
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"context"
|
||||
"slices"
|
||||
"strconv"
|
||||
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtscontext"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||
|
@ -77,6 +78,8 @@ func (c *Converter) AccountToExportStats(
|
|||
|
||||
// FollowingToCSV converts a slice of follows into
|
||||
// a slice of CSV-compatible Following records.
|
||||
//
|
||||
// Each follow should be populated.
|
||||
func (c *Converter) FollowingToCSV(
|
||||
ctx context.Context,
|
||||
following []*gtsmodel.Follow,
|
||||
|
@ -101,24 +104,19 @@ func (c *Converter) FollowingToCSV(
|
|||
thisDomain = config.GetHost()
|
||||
}
|
||||
|
||||
// Pre-sort the follows
|
||||
// by domain and username.
|
||||
slices.SortFunc(
|
||||
following,
|
||||
func(a *gtsmodel.Follow, b *gtsmodel.Follow) int {
|
||||
aStr := a.TargetAccount.Domain + "/" + a.TargetAccount.Username
|
||||
bStr := b.TargetAccount.Domain + "/" + b.TargetAccount.Username
|
||||
return cmp.Compare(aStr, bStr)
|
||||
},
|
||||
)
|
||||
|
||||
// For each item, add a record.
|
||||
for _, follow := range following {
|
||||
if follow.TargetAccount == nil {
|
||||
// Retrieve target account.
|
||||
var err error
|
||||
follow.TargetAccount, err = c.state.DB.GetAccountByID(
|
||||
// Barebones is fine here.
|
||||
gtscontext.SetBarebones(ctx),
|
||||
follow.TargetAccountID,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, gtserror.Newf(
|
||||
"db error getting target account for follow %s: %w",
|
||||
follow.ID, err,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
domain := follow.TargetAccount.Domain
|
||||
if domain == "" {
|
||||
// Local account,
|
||||
|
@ -144,6 +142,8 @@ func (c *Converter) FollowingToCSV(
|
|||
|
||||
// FollowersToCSV converts a slice of follows into
|
||||
// a slice of CSV-compatible Followers records.
|
||||
//
|
||||
// Each follow should be populated.
|
||||
func (c *Converter) FollowersToCSV(
|
||||
ctx context.Context,
|
||||
followers []*gtsmodel.Follow,
|
||||
|
@ -165,24 +165,19 @@ func (c *Converter) FollowersToCSV(
|
|||
thisDomain = config.GetHost()
|
||||
}
|
||||
|
||||
// Pre-sort the follows
|
||||
// by domain and username.
|
||||
slices.SortFunc(
|
||||
followers,
|
||||
func(a *gtsmodel.Follow, b *gtsmodel.Follow) int {
|
||||
aStr := a.Account.Domain + "/" + a.Account.Username
|
||||
bStr := b.Account.Domain + "/" + b.Account.Username
|
||||
return cmp.Compare(aStr, bStr)
|
||||
},
|
||||
)
|
||||
|
||||
// For each item, add a record.
|
||||
for _, follow := range followers {
|
||||
if follow.Account == nil {
|
||||
// Retrieve account.
|
||||
var err error
|
||||
follow.Account, err = c.state.DB.GetAccountByID(
|
||||
// Barebones is fine here.
|
||||
gtscontext.SetBarebones(ctx),
|
||||
follow.AccountID,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, gtserror.Newf(
|
||||
"db error getting account for follow %s: %w",
|
||||
follow.ID, err,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
domain := follow.Account.Domain
|
||||
if domain == "" {
|
||||
// Local account,
|
||||
|
@ -218,6 +213,15 @@ func (c *Converter) ListsToCSV(
|
|||
// CSV doesn't use column headers.
|
||||
records := make([][]string, 0)
|
||||
|
||||
// Pre-sort the lists
|
||||
// alphabetically.
|
||||
slices.SortFunc(
|
||||
lists,
|
||||
func(a *gtsmodel.List, b *gtsmodel.List) int {
|
||||
return cmp.Compare(a.Title, b.Title)
|
||||
},
|
||||
)
|
||||
|
||||
// For each item, add a record.
|
||||
for _, list := range lists {
|
||||
|
||||
|
@ -231,6 +235,17 @@ func (c *Converter) ListsToCSV(
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// Pre-sort the follows
|
||||
// by domain and username.
|
||||
slices.SortFunc(
|
||||
follows,
|
||||
func(a *gtsmodel.Follow, b *gtsmodel.Follow) int {
|
||||
aStr := a.TargetAccount.Domain + "/" + a.TargetAccount.Username
|
||||
bStr := b.TargetAccount.Domain + "/" + b.TargetAccount.Username
|
||||
return cmp.Compare(aStr, bStr)
|
||||
},
|
||||
)
|
||||
|
||||
// Append each follow as CSV record.
|
||||
for _, follow := range follows {
|
||||
var (
|
||||
|
@ -263,6 +278,8 @@ func (c *Converter) ListsToCSV(
|
|||
|
||||
// BlocksToCSV converts a slice of blocks into
|
||||
// a slice of CSV-compatible blocks records.
|
||||
//
|
||||
// Each block should be populated.
|
||||
func (c *Converter) BlocksToCSV(
|
||||
ctx context.Context,
|
||||
blocks []*gtsmodel.Block,
|
||||
|
@ -278,24 +295,19 @@ func (c *Converter) BlocksToCSV(
|
|||
// CSV doesn't use column headers.
|
||||
records := make([][]string, 0, len(blocks))
|
||||
|
||||
// Pre-sort the blocks
|
||||
// by domain and username.
|
||||
slices.SortFunc(
|
||||
blocks,
|
||||
func(a *gtsmodel.Block, b *gtsmodel.Block) int {
|
||||
aStr := a.TargetAccount.Domain + "/" + a.TargetAccount.Username
|
||||
bStr := b.TargetAccount.Domain + "/" + b.TargetAccount.Username
|
||||
return cmp.Compare(aStr, bStr)
|
||||
},
|
||||
)
|
||||
|
||||
// For each item, add a record.
|
||||
for _, block := range blocks {
|
||||
if block.TargetAccount == nil {
|
||||
// Retrieve target account.
|
||||
var err error
|
||||
block.TargetAccount, err = c.state.DB.GetAccountByID(
|
||||
// Barebones is fine here.
|
||||
gtscontext.SetBarebones(ctx),
|
||||
block.TargetAccountID,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, gtserror.Newf(
|
||||
"db error getting target account for block %s: %w",
|
||||
block.ID, err,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
domain := block.TargetAccount.Domain
|
||||
if domain == "" {
|
||||
// Local account,
|
||||
|
@ -315,6 +327,8 @@ func (c *Converter) BlocksToCSV(
|
|||
|
||||
// MutesToCSV converts a slice of mutes into
|
||||
// a slice of CSV-compatible mute records.
|
||||
//
|
||||
// Each mute should be populated.
|
||||
func (c *Converter) MutesToCSV(
|
||||
ctx context.Context,
|
||||
mutes []*gtsmodel.UserMute,
|
||||
|
@ -337,24 +351,19 @@ func (c *Converter) MutesToCSV(
|
|||
thisDomain = config.GetHost()
|
||||
}
|
||||
|
||||
// Pre-sort the mutes
|
||||
// by domain and username.
|
||||
slices.SortFunc(
|
||||
mutes,
|
||||
func(a *gtsmodel.UserMute, b *gtsmodel.UserMute) int {
|
||||
aStr := a.TargetAccount.Domain + "/" + a.TargetAccount.Username
|
||||
bStr := b.TargetAccount.Domain + "/" + b.TargetAccount.Username
|
||||
return cmp.Compare(aStr, bStr)
|
||||
},
|
||||
)
|
||||
|
||||
// For each item, add a record.
|
||||
for _, mute := range mutes {
|
||||
if mute.TargetAccount == nil {
|
||||
// Retrieve target account.
|
||||
var err error
|
||||
mute.TargetAccount, err = c.state.DB.GetAccountByID(
|
||||
// Barebones is fine here.
|
||||
gtscontext.SetBarebones(ctx),
|
||||
mute.TargetAccountID,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, gtserror.Newf(
|
||||
"db error getting target account for mute %s: %w",
|
||||
mute.ID, err,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
domain := mute.TargetAccount.Domain
|
||||
if domain == "" {
|
||||
// Local account,
|
||||
|
|
Loading…
Reference in a new issue