122 lines
4.9 KiB
Python
122 lines
4.9 KiB
Python
"""نامگذاری متریال — تفکیک «خانواده» از «مشخصه/واریانت» بهصورت عمومی.
|
||
|
||
برخی ردیفهای کاتالوگ، سایز/گرید را داخل نام ماده دارند
|
||
(«بلوک سیمانی ۲۰×۲۰×۴۰»، «لوله پلیاتیلن PE100 سایز ۲۵»). این ماژول با قواعد
|
||
الگویی (ابعاد NxNxN، کلیدواژههای مشخصه، کدهای گرید لاتین) نام را به
|
||
«خانواده» و «واریانت» میشکند — بدون هیچ هاردکدِ نام محصول مشخصی.
|
||
"""
|
||
import re
|
||
|
||
_DIGIT_MAP = str.maketrans("۰۱۲۳۴۵۶۷۸۹٠١٢٣٤٥٦٧٨٩", "01234567890123456789")
|
||
|
||
# واژههایی که شروع بخش مشخصه را نشان میدهند (ابعاد/درجه/تیپ/مدل/…)
|
||
_ATTRIBUTE_WORDS = {
|
||
"سایز",
|
||
"تیپ",
|
||
"مدل",
|
||
"درجه",
|
||
"ضخامت",
|
||
"قطر",
|
||
"ابعاد",
|
||
"طول",
|
||
"عرض",
|
||
"ارتفاع",
|
||
"رنگ",
|
||
"وزن",
|
||
"ظرفیت",
|
||
"شماره",
|
||
"کلاس",
|
||
}
|
||
|
||
# ابعاد ترکیبی: ۲۰×۲۰×۴۰ / ۲×۲٫۵ / 60x60 / 2*2.5
|
||
_DIMENSION_RE = re.compile(r"^\d+(?:[×xX*]\d+(?:[.,٫/]\d+)?)+$")
|
||
# کد گرید: PE100 / PN10 / DN50 / A3 / SN4 / ST37
|
||
_GRADE_RE = re.compile(r"^(?:PN|PE|DN|SN|SCH|ST|A|B|C)[-]?\d+$")
|
||
_NUMBER_RE = re.compile(r"^\d+(?:[.,٫/]\d+)?$")
|
||
_LATIN_WORD_RE = re.compile(r"^[A-Za-z]{2,6}$")
|
||
|
||
STANDARD_VARIANT_LABEL = "استاندارد"
|
||
|
||
|
||
def normalize_digits(text: str) -> str:
|
||
return (text or "").translate(_DIGIT_MAP)
|
||
|
||
|
||
def _is_spec_start(tokens: list[str], index: int) -> bool:
|
||
token = tokens[index]
|
||
normalized = normalize_digits(token)
|
||
if normalized in _ATTRIBUTE_WORDS: # «سایز»، «تیپ»، «مدل»، …
|
||
return True
|
||
if _DIMENSION_RE.match(normalized): # ۲۰×۲۰×۴۰
|
||
return True
|
||
if _GRADE_RE.match(token): # PE100 / PN10 / A3
|
||
return True
|
||
# کد لاتین کوتاه که پشتش عدد میآید: «IPE ۱۴»
|
||
if (
|
||
_LATIN_WORD_RE.match(token)
|
||
and index + 1 < len(tokens)
|
||
and _NUMBER_RE.match(normalize_digits(tokens[index + 1]))
|
||
):
|
||
return True
|
||
# عددِ تنها در انتهای نام (بدون کلیدواژه): «لوله مسی ۱۵»
|
||
if _NUMBER_RE.match(normalized) and index == len(tokens) - 1 and index >= 1:
|
||
return True
|
||
return False
|
||
|
||
|
||
def split_material_name(name: str) -> tuple[str, str | None]:
|
||
"""«لوله پلیاتیلن PE100 سایز ۲۵» → («لوله پلیاتیلن», «PE100 سایز ۲۵»)."""
|
||
tokens = [token for token in (name or "").split() if token]
|
||
for index in range(1, len(tokens)):
|
||
if _is_spec_start(tokens, index):
|
||
family = " ".join(tokens[:index]).strip()
|
||
variant = " ".join(tokens[index:]).strip()
|
||
if family and variant:
|
||
return family, variant
|
||
return " ".join(tokens).strip(), None
|
||
|
||
|
||
def material_family(name: str) -> str:
|
||
"""نام خانواده (بدون واریانت)."""
|
||
family, _ = split_material_name(name)
|
||
return " ".join(family.split())
|
||
|
||
|
||
def material_family_key(name: str) -> str:
|
||
"""شناسهی پایدار خانواده — همان نام نرمالشده (بدون UUID اختصاصی)."""
|
||
return material_family(name)
|
||
|
||
|
||
def variant_label(name: str) -> str:
|
||
"""برچسب واریانت؛ در نبود واریانت، «استاندارد»."""
|
||
_, variant = split_material_name(name)
|
||
return variant or STANDARD_VARIANT_LABEL
|
||
|
||
|
||
def spec_label_from_specs(specs: dict | None) -> str | None:
|
||
"""برچسب کوتاه مشخصات از JSON موجودی (فقط مقدارها، نه «کلید: مقدار»)."""
|
||
if not specs:
|
||
return None
|
||
values = [str(value).strip() for value in specs.values() if str(value).strip()]
|
||
return "، ".join(values) if values else None
|
||
|
||
|
||
def material_entry_labels(material_name: str, specs: dict | None) -> dict:
|
||
"""برچسبهای نمایشی یک ردیف کاتالوگ: خانواده، واریانت، مشخصه.
|
||
|
||
`spec_label` (برچسب محور X) = مقدار مشخصات JSON اگر موجود باشد، وگرنه
|
||
واریانتِ استخراجشده از نام، وگرنه «استاندارد».
|
||
"""
|
||
family = material_family(material_name)
|
||
variant = variant_label(material_name)
|
||
spec_from_json = spec_label_from_specs(specs)
|
||
# برچسب محور X: واریانت (سایز/گرید) ترجیح دارد؛ در نبود آن مقدار مشخصات
|
||
axis_label = variant if variant != STANDARD_VARIANT_LABEL else (spec_from_json or STANDARD_VARIANT_LABEL)
|
||
return {
|
||
"family_id": material_family_key(material_name),
|
||
"family_name": family,
|
||
"variant_label": variant,
|
||
"spec_label": axis_label,
|
||
"spec_detail": spec_from_json,
|
||
}
|