"""Wiki page titles and transliterated wiki filenames.""" from __future__ import annotations import hashlib import re from export1c_help.discover import HelpPage _INVALID = re.compile(r'[\\/:*?"<>|#\[\]]+') _SPACES = re.compile(r"\s+") _NON_SLUG = re.compile(r"[^a-z0-9._-]+") _DASH_RUNS = re.compile(r"-+") _MAX_SLUG = 48 _TRANSLIT = { "а": "a", "б": "b", "в": "v", "г": "g", "д": "d", "е": "e", "ё": "e", "ж": "zh", "з": "z", "и": "i", "й": "y", "к": "k", "л": "l", "м": "m", "н": "n", "о": "o", "п": "p", "р": "r", "с": "s", "т": "t", "у": "u", "ф": "f", "х": "h", "ц": "ts", "ч": "ch", "ш": "sh", "щ": "sch", "ъ": "", "ы": "y", "ь": "", "э": "e", "ю": "yu", "я": "ya", } def sanitize_title(title: str) -> str: title = _SPACES.sub(" ", title).strip() title = _INVALID.sub(" ", title) title = _SPACES.sub(" ", title).strip() return title or "untitled" def _short_hash(s: str) -> str: return hashlib.sha1(s.encode("utf-8")).hexdigest()[:8] def _translit_slug(title: str) -> str: chars: list[str] = [] for ch in title.lower(): chars.append(_TRANSLIT.get(ch, ch)) raw = "".join(chars).replace(" ", "-") raw = _NON_SLUG.sub("-", raw) raw = _DASH_RUNS.sub("-", raw).strip("-._") return raw or "page" def wiki_filename(title: str, *, meta_key: str | None = None) -> str: """ Build ASCII transliterated ``slug.md`` with bounded slug length. """ slug = _translit_slug(sanitize_title(title)) if len(slug) <= _MAX_SLUG: return f"{slug}.md" suffix = "-" + _short_hash(meta_key or title) keep = max(8, _MAX_SLUG - len(suffix)) slug = slug[:keep].rstrip("-._") or "page" return f"{slug}{suffix}.md" def decode_wiki_filename(name: str) -> str: return name[:-3] if name.endswith(".md") else name def assign_titles( pages: list[HelpPage], h1_by_meta: dict[str, str | None], ) -> tuple[dict[str, str], dict[str, str]]: """Returns (meta_key → title, meta_key → filename).""" used_title: set[str] = set() used_file: set[str] = set() titles: dict[str, str] = {} files: dict[str, str] = {} for p in pages: h1 = h1_by_meta.get(p.meta_key) base = sanitize_title(h1 or p.synonym or p.meta_key) title = base n = 2 while title.casefold() in used_title: if p.is_form and p.form_name and n == 2: title = sanitize_title(f"{base} — {p.form_name}") elif n == 2: title = sanitize_title(f"{base} — {p.object_name}") else: title = sanitize_title(f"{base} ({n})") n += 1 if n > 5000: title = sanitize_title(p.meta_key) break fname = wiki_filename(title, meta_key=p.meta_key) if fname.casefold() in used_file: fname = wiki_filename( f"{title}-{_short_hash(p.meta_key)}", meta_key=p.meta_key, ) guard = 0 while fname.casefold() in used_file: fname = f"page-{_short_hash(p.meta_key + str(guard))}.md" guard += 1 used_title.add(title.casefold()) used_file.add(fname.casefold()) titles[p.meta_key] = title files[p.meta_key] = fname return titles, files