Files
GostGenerator/web/backend/app/services/designators.py
T
2026-09-02 12:48:03 +03:00

179 lines
5.9 KiB
Python

"""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)