summaryrefslogtreecommitdiff
path: root/internal/typeutils/csv.go
diff options
context:
space:
mode:
authorLibravatar tobi <31960611+tsmethurst@users.noreply.github.com>2024-09-18 12:23:28 +0200
committerLibravatar GitHub <noreply@github.com>2024-09-18 10:23:28 +0000
commitf819229988bb79de709abbfb438831b1e452afd4 (patch)
tree3459f3c82d9885e6773ba78073c5560d529e031b /internal/typeutils/csv.go
parent[chore/frontend] Tweak status styling a little (#3317) (diff)
downloadgotosocial-f819229988bb79de709abbfb438831b1e452afd4.tar.xz
[chore] make csv export ordering determinate (#3318)
Diffstat (limited to 'internal/typeutils/csv.go')
-rw-r--r--internal/typeutils/csv.go139
1 files changed, 74 insertions, 65 deletions
diff --git a/internal/typeutils/csv.go b/internal/typeutils/csv.go
index ae080a63f..7211d5c9c 100644
--- a/internal/typeutils/csv.go
+++ b/internal/typeutils/csv.go
@@ -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,