refactor(collections): rule evaluators take the real row; both 45-field converter twins deleted

checkRulesAgainstBook/evaluateRule and the service's EvaluateRules
existed in two parallel universes: one over database.MediaItems (what
every query actually returns) and one over ListMediaItemsRow (what two
hand-written 45-field mediaItemsToListMediaItemsRow converters
produced — verbatim twins in the handler and the dashboard service,
with a URL-resolution side effect baked into a "converter"). The rule
fields (genre/series/author/language/publisher/copyright_year/tags)
all exist on MediaItems, so the evaluators now take the real row and
the converters, their three call-site loops, and the dead
ApplyAutoAssignRules entry point (zero callers) are gone.

The rules surface is wire-covered by the collections rules scenarios;
handlers+services suites and the full pin/H1/H3 battery green.
This commit is contained in:
John O'Keefe committed 2026-10-17 16:48:39 -04:00
1 parent b47a2b86a7
commit 18727fef93
7 files changed
+54 -193

No files matched your search

+1 -35
View File
@@ -509,7 +509,7 @@ func (s *CollectionService) mediaItemMatchesRule(item database.MediaItems, rule
}
// EvaluateRules evaluates auto-assign rules for a media item
func (s *CollectionService) EvaluateRules(mediaItem database.ListMediaItemsRow, rules []Rule) []RuleEvaluation {
func (s *CollectionService) EvaluateRules(mediaItem database.MediaItems, rules []Rule) []RuleEvaluation {
var evaluations []RuleEvaluation
// Sort rules by priority (higher priority first)
@@ -708,40 +708,6 @@ func (s *CollectionService) DeleteDeviceShelfMapping(ctx context.Context, mappin
return nil
}
// ApplyAutoAssignRules applies auto-assign rules for all collections to a media item
func (s *CollectionService) ApplyAutoAssignRules(ctx context.Context, mediaItem database.ListMediaItemsRow, userID uuid.UUID) ([]uuid.UUID, error) {
var matchedCollectionIDs []uuid.UUID
collections, err := s.GetUserCollections(ctx, userID)
if err != nil {
return nil, fmt.Errorf("failed to get user collections: %v", err)
}
for _, collection := range collections {
if len(collection.AutoAssignRules) == 0 {
continue
}
var rules []Rule
if err := json.Unmarshal(collection.AutoAssignRules, &rules); err != nil {
continue
}
evaluations := s.EvaluateRules(mediaItem, rules)
// Check if any rules match
for _, eval := range evaluations {
if eval.Matches && eval.Confidence > 0.7 {
matchedCollectionIDs = append(matchedCollectionIDs, uuid.UUID(collection.ID.Bytes))
break
}
}
}
return matchedCollectionIDs, nil
}
// Helper functions (Go standard library functions)
func toLower(s string) string {
if len(s) == 0 {
return s