Source code for yasuki_core.install.utils

import re
import unicodedata
from unidecode import unidecode_expect_ascii

from yasuki_core.paths import resolve_set_image_path

SUFFIX_MAP = {
    "experienced": "exp",
    "inexperienced": "inexp",
    "experiencedcom": "exp_com",
    "experienced 2cw": "exp_2_cw",
    "experienced2kyd": "exp2kyd",
    "experienced 2": "exp2",
    "experienced 3": "exp3",
    "experienced 4": "exp4",
}

DECK_MAP = {
    "Fate": "FATE",
    "Dynasty": "DYNASTY",
    "Pre-Game": "PRE_GAME",
    "Other": "OTHER",
}


[docs] def clean_string(s): # Remove commas from numbers > 999 s = re.sub(r"(?<=\d),(?=\d{3}\b)", "", s) # Handle special characters where I have a strong opinion on the replacement s = ( unidecode_expect_ascii(s) .lower() .strip() .replace(",", " ") .replace("'", "") .replace("&", "and") ) # Remaining special characters are simply removed s = re.sub(r"[^a-z0-9_]", " ", s).strip() s = re.sub(" +", " ", s) s = s.replace(" ", "_") return s
[docs] def normalize_name(name: str) -> str: """ Create lowercase ASCII version of name for searching and sorting. Removes diacritics and converts to lowercase. Parameters ---------- name : str Name to normalize Returns ------- normalized : str Lowercase ASCII version """ nfkd = unicodedata.normalize("NFKD", name) stripped = "".join(ch for ch in nfkd if not unicodedata.combining(ch)) return stripped.lower()
[docs] def normalize_for_filesystem(name: str) -> str: """ Normalize name for file system paths. Converts to lowercase, replaces special characters with underscores, handles numeric formatting, and ensures filesystem safety. Parameters ---------- name : str Name to normalize Returns ------- normalized : str Filesystem-safe string with only lowercase alphanumeric and underscores """ name = re.sub(r"(?<=\d),(?=\d{3}\b)", "", name) normalized = name.lower() normalized = normalized.replace(",", " ").replace("'", "").replace("&", "and") normalized = re.sub(r"[^a-z0-9]+", "_", normalized) normalized = normalized.strip("_") return normalized
[docs] def strip_title(title: str) -> str: """ Convert Extended Title to filename-safe format. Handles experience markers by splitting on bullet point and mapping experience keywords to short codes. Examples -------- "Bayushi Kachiko" → "bayushi_kachiko" "Bayushi Kachiko • Experienced" → "bayushi_kachiko_exp" "Bayushi Kachiko • Inexperienced" → "bayushi_kachiko_inexp" "Bayushi Kachiko • Experienced 2" → "bayushi_kachiko_exp2" Parameters ---------- title : str Extended title with optional experience markers Returns ------- filename : str Normalized filename without extension """ if "•" not in title: return normalize_for_filesystem(title) title, tags = title.split("•", 1) title = normalize_for_filesystem(title) tags = [ SUFFIX_MAP.get( normalize_for_filesystem(stripped_tag), normalize_for_filesystem(stripped_tag) ) for tag in tags.split(" ") if len(stripped_tag := tag.strip().lower()) > 0 ] return "_".join([title, *tags])
[docs] def find_card_image(extended_title: str, set_name: str) -> str | None: """ Find image file for a card using Extended Title. Images are stored in: <SETS_DIR>/<set_name>/<card_id>.png Parameters ---------- extended_title : str Extended Title field (e.g., "Bayushi Kachiko • Experienced") set_name : str Set name Returns ------- image_path : str or None Relative path like "sets/celestial_edition/card.png", or None if not found """ path = expected_card_image_path(extended_title, set_name) if path is None: return None card_path = resolve_set_image_path(path) if card_path is not None and card_path.exists(): return path return None
[docs] def expected_card_image_path(extended_title: str, set_name: str) -> str | None: """ Compute the expected image path for a card without checking the filesystem. Parameters ---------- extended_title : str Extended Title field (e.g., "Bayushi Kachiko • Experienced") set_name : str Set name Returns ------- image_path : str or None Relative path like "sets/celestial_edition/a_legion_of_one.png", or None if inputs are empty """ if not extended_title or not set_name: return None set_dir_name = normalize_for_filesystem(set_name) card_file_name = strip_title(extended_title) + ".png" return f"sets/{set_dir_name}/{card_file_name}"
[docs] def process_string(s: str) -> str: """ Clean up whitespace and special characters from scraped HTML text. Normalizes non-breaking spaces, newlines, and multiple spaces. Parameters ---------- s : str Raw string from HTML Returns ------- cleaned : str String with normalized whitespace """ s = s.strip() s = re.sub(r"[\xa0\n]", " ", s) s = re.sub(" +", " ", s) return s
[docs] def normalize_empty(value: str | None) -> str | None: """ Convert empty strings and dash placeholders to None for SQL NULL. Parameters ---------- value : str or None Value to normalize Returns ------- normalized : str or None None if value is empty/dash, otherwise original value """ if value is None: return None if isinstance(value, str) and (value.strip() == "" or value == "-"): return None return value