refactor: the server says a count one way, words.Count, for notifications, a wanted match's reason and the duplicate resolver's activity line (M505 #5643)
release / govulncheck (push) Successful in 22s
release / web (push) Successful in 1m35s
release / go (push) Successful in 1m45s
release / integration (push) Successful in 5m41s
release / android (push) Successful in 6m7s
release / Build signed APK (releases and dev) (push) Successful in 6m26s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 29s
release / Verify release artifacts (tag releases only) (push) Skipped
release / govulncheck (push) Successful in 22s
release / web (push) Successful in 1m35s
release / go (push) Successful in 1m45s
release / integration (push) Successful in 5m41s
release / android (push) Successful in 6m7s
release / Build signed APK (releases and dev) (push) Successful in 6m26s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 29s
release / Verify release artifacts (tag releases only) (push) Skipped
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,7 @@ import (
|
|||||||
"git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq"
|
"git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq"
|
||||||
"git.fabledsword.com/bvandeusen/minstrel/internal/notifications"
|
"git.fabledsword.com/bvandeusen/minstrel/internal/notifications"
|
||||||
syncpkg "git.fabledsword.com/bvandeusen/minstrel/internal/sync"
|
syncpkg "git.fabledsword.com/bvandeusen/minstrel/internal/sync"
|
||||||
|
"git.fabledsword.com/bvandeusen/minstrel/internal/words"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Duplicate resolver (M498 #5435, #5437; M502 #5517).
|
// Duplicate resolver (M498 #5435, #5437; M502 #5517).
|
||||||
@@ -177,7 +178,7 @@ func ResolveDuplicates(
|
|||||||
if res.Merged > 0 {
|
if res.Merged > 0 {
|
||||||
notifyAdmins(ctx, notifications.KindDuplicatesResolved, notifications.Payload{
|
notifyAdmins(ctx, notifications.KindDuplicatesResolved, notifications.Payload{
|
||||||
Count: int64(res.Merged),
|
Count: int64(res.Merged),
|
||||||
Detail: fmt.Sprintf("Merged %d duplicate %s.", res.Merged, pluralWord(res.Merged, "copy", "copies")),
|
Detail: "Merged " + words.Count(res.Merged, "duplicate copy", "duplicate copies") + ".",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return res, nil
|
return res, nil
|
||||||
@@ -351,13 +352,6 @@ func autoMerge(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func pluralWord(n int, one, many string) string {
|
|
||||||
if n == 1 {
|
|
||||||
return one
|
|
||||||
}
|
|
||||||
return many
|
|
||||||
}
|
|
||||||
|
|
||||||
// DuplicateResolveWorker runs a resolver pass every hour.
|
// DuplicateResolveWorker runs a resolver pass every hour.
|
||||||
type DuplicateResolveWorker struct {
|
type DuplicateResolveWorker struct {
|
||||||
pool *pgxpool.Pool
|
pool *pgxpool.Pool
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package notifications
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"git.fabledsword.com/bvandeusen/minstrel/internal/words"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Rendered is a notification as people read it. The server renders it once,
|
// Rendered is a notification as people read it. The server renders it once,
|
||||||
@@ -138,13 +140,10 @@ func Render(kind Kind, payload []byte) Rendered {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func plural(n int64, one, many string) string {
|
func plural(n int64, one, many string) string {
|
||||||
if n == 1 {
|
|
||||||
return "1 " + one
|
|
||||||
}
|
|
||||||
if n < 1 {
|
if n < 1 {
|
||||||
// A coalesced kind always carries a count; this only guards a
|
// A coalesced kind always carries a count; this only guards a
|
||||||
// malformed row from reading "0 tracks went missing".
|
// malformed row from reading "0 tracks went missing".
|
||||||
return "Some " + many
|
return "Some " + many
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%d %s", n, many)
|
return words.Count(n, one, many)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"git.fabledsword.com/bvandeusen/minstrel/internal/musicbrainz"
|
"git.fabledsword.com/bvandeusen/minstrel/internal/musicbrainz"
|
||||||
"git.fabledsword.com/bvandeusen/minstrel/internal/release"
|
"git.fabledsword.com/bvandeusen/minstrel/internal/release"
|
||||||
"git.fabledsword.com/bvandeusen/minstrel/internal/tags"
|
"git.fabledsword.com/bvandeusen/minstrel/internal/tags"
|
||||||
|
"git.fabledsword.com/bvandeusen/minstrel/internal/words"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Matching a library album to its MusicBrainz release group (#5569).
|
// Matching a library album to its MusicBrainz release group (#5569).
|
||||||
@@ -262,8 +263,8 @@ func (s *Service) matchAlbum(ctx context.Context, a dbq.ListAlbumsToMatchRow, tr
|
|||||||
if considered == 0 {
|
if considered == 0 {
|
||||||
return albumMatch{Detail: fmt.Sprintf("MusicBrainz has no album called %q by %s.", a.Title, a.ArtistName)}, nil
|
return albumMatch{Detail: fmt.Sprintf("MusicBrainz has no album called %q by %s.", a.Title, a.ArtistName)}, nil
|
||||||
}
|
}
|
||||||
detail := fmt.Sprintf("MusicBrainz has %d %s called %q by %s, but none has its tracks: the closest, %q, agrees on %d of its %d",
|
detail := fmt.Sprintf("MusicBrainz has %s called %q by %s, but none has its tracks: the closest, %q, agrees on %d of its %d",
|
||||||
considered, plural(considered, "album", "albums"), a.Title, a.ArtistName, best.Title, max(bestN, 0), len(tracks))
|
words.Count(considered, "album", "albums"), a.Title, a.ArtistName, best.Title, max(bestN, 0), len(tracks))
|
||||||
switch {
|
switch {
|
||||||
case bestN < 0:
|
case bestN < 0:
|
||||||
detail += " (MusicBrainz lists no edition of it)"
|
detail += " (MusicBrainz lists no edition of it)"
|
||||||
@@ -403,10 +404,3 @@ func abs(n int) int {
|
|||||||
}
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
func plural(n int, one, many string) string {
|
|
||||||
if n == 1 {
|
|
||||||
return one
|
|
||||||
}
|
|
||||||
return many
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// Package words says counts in English for what the server writes to people:
|
||||||
|
// notifications, emails, the reasons a scan or a match gives.
|
||||||
|
package words
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
// Count says n with the noun that agrees with it: "1 track", "3 tracks". The
|
||||||
|
// noun may carry its verb ("track went missing" / "tracks went missing").
|
||||||
|
// The web app's plural() is the same thing on the other side (M505).
|
||||||
|
func Count[N ~int | ~int32 | ~int64](n N, one, many string) string {
|
||||||
|
if n == 1 {
|
||||||
|
return "1 " + one
|
||||||
|
}
|
||||||
|
return strconv.FormatInt(int64(n), 10) + " " + many
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package words
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestCount(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
got, want string
|
||||||
|
}{
|
||||||
|
{Count(1, "track", "tracks"), "1 track"},
|
||||||
|
{Count(0, "track", "tracks"), "0 tracks"},
|
||||||
|
{Count(int64(3), "track went missing", "tracks went missing"), "3 tracks went missing"},
|
||||||
|
{Count(int32(-1), "copy", "copies"), "-1 copies"},
|
||||||
|
}
|
||||||
|
for _, c := range cases {
|
||||||
|
if c.got != c.want {
|
||||||
|
t.Errorf("got %q, want %q", c.got, c.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user