"""Default designator type names (singular/plural) by letter prefix.""" from __future__ import annotations import re # Minimal useful defaults; projects can override via DB DEFAULT_MAPPINGS: dict[str, tuple[str, str]] = { "C": ("Конденсатор", "Конденсаторы"), "R": ("Резистор", "Резисторы"), "L": ("Катушка индуктивности", "Катушки индуктивности"), "D": ("Диод", "Диоды"), "VD": ("Диод", "Диоды"), "VT": ("Транзистор", "Транзисторы"), "Q": ("Транзистор", "Транзисторы"), "DA": ("Микросхема", "Микросхемы"), "DD": ("Микросхема", "Микросхемы"), "U": ("Микросхема", "Микросхемы"), "X": ("Соединитель", "Соединители"), "XP": ("Соединитель", "Соединители"), "XS": ("Соединитель", "Соединители"), "FU": ("Предохранитель", "Предохранители"), "F": ("Предохранитель", "Предохранители"), "SA": ("Переключатель", "Переключатели"), "SB": ("Кнопка", "Кнопки"), "HL": ("Индикатор", "Индикаторы"), "HG": ("Индикатор", "Индикаторы"), "G": ("Генератор", "Генераторы"), "T": ("Трансформатор", "Трансформаторы"), "TV": ("Трансформатор", "Трансформаторы"), "K": ("Реле", "Реле"), "B": ("Пьезоэлемент", "Пьезоэлементы"), "Z": ("Фильтр", "Фильтры"), } def split_designator(designator: str) -> tuple[str, str]: m = re.match(r"^([A-Za-z]+)(\d*)$", designator.strip()) if m: return m.group(1), m.group(2) for i, ch in enumerate(designator): if ch.isdigit(): return designator[:i], designator[i:] return designator, "" def designator_sort_key(designator: str) -> tuple[str, int, str]: letter, num = split_designator(designator) try: n = int(num) if num else 0 except ValueError: n = 0 return letter, n, num return letter.upper(), n, num def prefix_from_designator(designator: str) -> str: letter, _ = split_designator(designator) return letter.upper() def get_type_names( designator: str, mappings: dict[str, tuple[str, str]] | None = None ) -> tuple[str, str]: maps = {**DEFAULT_MAPPINGS, **(mappings or {})} prefix = prefix_from_designator(designator) if prefix in maps: return maps[prefix] # try 2-letter then 1-letter if len(prefix) >= 2 and prefix[:2] in maps: return maps[prefix[:2]] if prefix[:1] in maps: return maps[prefix[:1]] return (prefix, prefix) def format_designator_range(designators: list[str]) -> str: if not designators: return "" if len(designators) == 1: return designators[0] if len(designators) == 2: return f"{designators[0]}, {designators[1]}" return f"{designators[0]}-{designators[-1]}" def format_consecutive_ranges(designators: list[str]) -> str: """Group consecutive numbered designators into ranges.""" if not designators: return "" parsed = [] for d in designators: letter, num = split_designator(d) try: n = int(num) if num else None except ValueError: n = None parsed.append((d, letter, n)) segments: list[list[str]] = [] current: list[str] = [] last_letter = None last_num = None for d, letter, n in parsed: if not current: current = [d] last_letter, last_num = letter, n continue if letter == last_letter and n is not None and last_num is not None and n == last_num + 1: current.append(d) last_num = n else: segments.append(current) current = [d] last_letter, last_num = letter, n if current: segments.append(current) parts = [] for seg in segments: if len(seg) == 1: parts.append(seg[0]) elif len(seg) == 2: parts.append(f"{seg[0]}, {seg[1]}") else: parts.append(f"{seg[0]}-{seg[-1]}") return ", ".join(parts) def split_long_text(text: str, max_length: int) -> list[str]: if not text or len(text) <= max_length: return [text] if text is not None else [""] parts: list[str] = [] remaining = text while len(remaining) > max_length: chunk = remaining[:max_length] split_at = chunk.rfind(",") if split_at < max_length // 3: split_at = chunk.rfind(" ") if split_at < max_length // 3: split_at = max_length parts.append(remaining[:split_at].rstrip(", ").strip()) remaining = remaining[split_at:].lstrip(", ").strip() if remaining: parts.append(remaining) return parts or [""] def resolve_expression(value: str, props: dict[str, str]) -> str: if not value.startswith("="): return value expr = value[1:] result = [] i = 0 while i < len(expr): ch = expr[i] if ch in "'\"": quote = ch i += 1 start = i while i < len(expr) and expr[i] != quote: i += 1 result.append(expr[start:i]) i += 1 elif ch == "+": i += 1 elif ch.isspace(): i += 1 else: start = i while i < len(expr) and expr[i] not in "+'\"": i += 1 key = expr[start:i].strip() val = props.get(key, "") if val.startswith("="): val = resolve_expression(val, props) result.append(val) return "".join(result)