fix(tags): case-insensitive kind prefix + (Fandom) suffix rescue on add
parse_kind_prefix now lowercases the prefix before checking KNOWN_KINDS, matching the modal JS which already lowercases before its visibility check. Without this, typing 'Character:Jinx ...' showed the fandom picker but the server fell back to kind=user. Both /tags/add and /api/images/bulk-add-tag now also split a trailing '(Fandom)' suffix from a typed character name when no explicit fandom is staged, so a manual entry like 'Character:Jinx (League Of Legends)' produces a clean (name='Jinx', fandom_id=<league>) row instead of a malformed character with the suffix baked into the name. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+22
@@ -1189,6 +1189,17 @@ def add_tag(image_id):
|
|||||||
fandom_id = request.form.get("fandom_id", type=int)
|
fandom_id = request.form.get("fandom_id", type=int)
|
||||||
fandom_name = (request.form.get("fandom_name") or "").strip() or None
|
fandom_name = (request.form.get("fandom_name") or "").strip() or None
|
||||||
|
|
||||||
|
# If the user typed the name with a "(Fandom)" suffix and didn't
|
||||||
|
# use the fandom picker, split the suffix into a real fandom
|
||||||
|
# association so we don't create a malformed character row with
|
||||||
|
# the suffix baked into the name and fandom_id=NULL.
|
||||||
|
import re as _re
|
||||||
|
if not fandom_id and not fandom_name:
|
||||||
|
m = _re.match(r'^(.+?) \(([^()]+)\)$', name)
|
||||||
|
if m:
|
||||||
|
name = m.group(1).strip()
|
||||||
|
fandom_name = m.group(2).strip()
|
||||||
|
|
||||||
if fandom_id:
|
if fandom_id:
|
||||||
fandom_tag = Tag.query.filter_by(id=fandom_id, kind="fandom").first()
|
fandom_tag = Tag.query.filter_by(id=fandom_id, kind="fandom").first()
|
||||||
if fandom_tag is None:
|
if fandom_tag is None:
|
||||||
@@ -2615,6 +2626,17 @@ def bulk_add_tag():
|
|||||||
if kind == "character":
|
if kind == "character":
|
||||||
fandom_id = data.get("fandom_id")
|
fandom_id = data.get("fandom_id")
|
||||||
fandom_name = (data.get("fandom_name") or "").strip() or None
|
fandom_name = (data.get("fandom_name") or "").strip() or None
|
||||||
|
|
||||||
|
# Same suffix-rescue as /tags/add: typed "Jinx (League Of Legends)"
|
||||||
|
# without an explicit fandom should still produce a clean character
|
||||||
|
# row with a real fandom_id, not a malformed name.
|
||||||
|
import re as _re
|
||||||
|
if not fandom_id and not fandom_name:
|
||||||
|
m = _re.match(r'^(.+?) \(([^()]+)\)$', name)
|
||||||
|
if m:
|
||||||
|
name = m.group(1).strip()
|
||||||
|
fandom_name = m.group(2).strip()
|
||||||
|
|
||||||
if fandom_id:
|
if fandom_id:
|
||||||
fandom_tag = Tag.query.filter_by(id=fandom_id, kind="fandom").first()
|
fandom_tag = Tag.query.filter_by(id=fandom_id, kind="fandom").first()
|
||||||
if fandom_tag is None:
|
if fandom_tag is None:
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ KNOWN_KINDS: frozenset[str] = frozenset({
|
|||||||
def parse_kind_prefix(raw: str) -> tuple[str | None, str]:
|
def parse_kind_prefix(raw: str) -> tuple[str | None, str]:
|
||||||
"""Split a raw user-typed tag string into (kind, name).
|
"""Split a raw user-typed tag string into (kind, name).
|
||||||
|
|
||||||
|
The kind prefix is matched case-insensitively so 'Character:' and
|
||||||
|
'CHARACTER:' resolve the same as 'character:'. The returned kind is
|
||||||
|
always lowercase canonical form. The modal JS already lowercases
|
||||||
|
before its own prefix check; this keeps the server in sync.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
(kind, name) where kind is one of KNOWN_KINDS, or
|
(kind, name) where kind is one of KNOWN_KINDS, or
|
||||||
(None, raw.strip()) if no recognized prefix is present.
|
(None, raw.strip()) if no recognized prefix is present.
|
||||||
@@ -32,12 +37,14 @@ def parse_kind_prefix(raw: str) -> tuple[str | None, str]:
|
|||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
parse_kind_prefix('character:Saber') -> ('character', 'Saber')
|
parse_kind_prefix('character:Saber') -> ('character', 'Saber')
|
||||||
|
parse_kind_prefix('Character:Saber') -> ('character', 'Saber')
|
||||||
parse_kind_prefix('sunset') -> (None, 'sunset')
|
parse_kind_prefix('sunset') -> (None, 'sunset')
|
||||||
parse_kind_prefix('http://example') -> (None, 'http://example')
|
parse_kind_prefix('http://example') -> (None, 'http://example')
|
||||||
parse_kind_prefix('artist: Eric ') -> ('artist', 'Eric')
|
parse_kind_prefix('artist: Eric ') -> ('artist', 'Eric')
|
||||||
"""
|
"""
|
||||||
if ':' in raw:
|
if ':' in raw:
|
||||||
prefix, rest = raw.split(':', 1)
|
prefix, rest = raw.split(':', 1)
|
||||||
if prefix in KNOWN_KINDS:
|
prefix_lower = prefix.lower()
|
||||||
return prefix, rest.strip()
|
if prefix_lower in KNOWN_KINDS:
|
||||||
|
return prefix_lower, rest.strip()
|
||||||
return None, raw.strip()
|
return None, raw.strip()
|
||||||
|
|||||||
Reference in New Issue
Block a user