"""Section-scoped and continuous object numbering.""" from collections import defaultdict APPENDIX_LETTERS = "АБВГДЕЖИКЛМНПРСТУФХЦШЩЭЮЯ" # without Ё З Й О Ч Ь Ы Ъ class Numberer: """Assigns object numbers. * ``section`` (MIREA): ``1.1``, ``2.3`` or ``Б.1`` — counters reset per section/appendix. * ``continuous`` (PIS_custom): ``1``, ``2``, ``3`` — сквозная нумерация по всему документу. """ def __init__(self, mode: str = "section"): if mode not in ("section", "continuous"): raise ValueError(f"Unknown numbering mode: {mode!r}") self._mode = mode self._section: int = 0 self._appendix: str | None = None self._counters: dict[str, int] = defaultdict(int) self._labels: dict[tuple[str, str], str] = {} # (category, unique_name) -> number string @property def mode(self) -> str: return self._mode @property def section(self) -> int: return self._section @property def appendix(self) -> str | None: return self._appendix def enter_section(self, section_number: int) -> None: """Start a numbered chapter (Heading 1 numbered).""" self._section = section_number self._appendix = None if self._mode != "continuous": self._counters.clear() def enter_appendix(self, letter: str) -> None: """Start an appendix (letter А, Б, …).""" self._appendix = letter if self._mode != "continuous": self._counters.clear() def next_number(self, category: str, unique_name: str | None = None) -> str: """Allocate next number for category; optionally bind a label.""" self._counters[category] += 1 n = self._counters[category] if self._mode == "continuous": number = str(n) elif self._appendix: number = f"{self._appendix}.{n}" elif self._section: number = f"{self._section}.{n}" else: number = str(n) if unique_name: self._labels[(category, unique_name)] = number return number def resolve(self, category: str, unique_name: str) -> str | None: return self._labels.get((category, unique_name)) def register_label(self, category: str, unique_name: str, number: str) -> None: self._labels[(category, unique_name)] = number