feat: ML tag suggestions, character/fandom integrity, underscores, modal polish
Consolidated merge of feat/tag-suggestions branch. Original 64-commit history was lost to git-object corruption in a Nextcloud-synced checkout; this single commit captures the equivalent diff. Includes: - pgvector-backed tag suggestion infra (WD14 + SigLIP centroids, ml-worker container, Celery tasks, suggestion service, accept/reject endpoints + modal UI with green/red chip buttons) - Character/fandom integrity: title-case normalization on every write path, fandom-id backfill, maintenance task + settings button, migrations g26041901 + h26041901 to canonicalize legacy rows with case-only duplicate merging - Tag-underscores + modal polish: WD14 name canonicalization at emit + accept + add/bulk-add paths, migration i26041901 for legacy-row rename-or-merge across character/fandom/NULL kinds, suggestion-accept refresh parity via awaited loadTags, persistent chip tint
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
"""Normalization helpers for Tag display names.
|
||||
|
||||
Keeps the first letter of every whitespace-separated word upper-case and
|
||||
preserves internal punctuation. Called from every character/fandom write
|
||||
path so typos like "misty" don't accumulate alongside "Misty".
|
||||
"""
|
||||
|
||||
|
||||
def underscores_to_spaces(s: str) -> str:
|
||||
"""Replace every underscore with a space.
|
||||
|
||||
Used by general/null-kind write paths and as a pre-step inside
|
||||
normalize_display_name so WD14's 'misty_ketchum' and a hand-typed
|
||||
'Misty Ketchum' collapse onto the same tag name.
|
||||
|
||||
Examples:
|
||||
"big_hair" -> "big hair"
|
||||
"long_blue_hair" -> "long blue hair"
|
||||
"" -> ""
|
||||
"""
|
||||
return s.replace('_', ' ') if s else s
|
||||
|
||||
|
||||
def normalize_display_name(s: str) -> str:
|
||||
"""Title-case each whitespace-separated word. Preserves internal punctuation.
|
||||
|
||||
Underscores are converted to spaces first so WD14-style names normalize
|
||||
identically to hand-typed names.
|
||||
|
||||
Examples:
|
||||
"misty" -> "Misty"
|
||||
"misty ketchum" -> "Misty Ketchum"
|
||||
"misty_ketchum" -> "Misty Ketchum"
|
||||
"o'brien" -> "O'brien"
|
||||
"mary-kate" -> "Mary-kate"
|
||||
"" -> ""
|
||||
" " -> " " (only whitespace — returned unchanged)
|
||||
"""
|
||||
if not s:
|
||||
return s
|
||||
s = underscores_to_spaces(s)
|
||||
parts = s.split(' ')
|
||||
out: list[str] = []
|
||||
for p in parts:
|
||||
if p == '':
|
||||
out.append(p)
|
||||
else:
|
||||
out.append(p[:1].upper() + p[1:])
|
||||
return ' '.join(out)
|
||||
Reference in New Issue
Block a user