Support incomplete model suffixes

This commit is contained in:
Fiden committed 2026-08-07 18:47:52 +03:00
1 parent 7874c79534
commit 4286a92fcc
5 files changed
+108 -5

No files matched your search

+36 -1
View File
@@ -44,6 +44,7 @@ _MODEL_RE = re.compile(
r"(?<![a-zа-я0-9])([a-zа-я]{2,6}-\d+[a-zа-я]?|[a-zа-я]{2,6}\d+[a-zа-я]?)(?![a-zа-я0-9])",
re.IGNORECASE,
)
_NORMALIZED_MODEL_RE = re.compile(r"^([a-zа-я]{2,6})(\d+)([a-zа-я]?)$", re.IGNORECASE)
_PAIR_DIMENSION_TYPES = frozenset(
{
@@ -229,6 +230,31 @@ def _extract_model_codes(normalized: str) -> frozenset[str]:
return frozenset(codes)
def model_code_matches(query_code: str, candidate_code: str) -> bool:
"""Match a complete model or a model with only its final letter omitted.
Numeric parts remain exact. Thus ``арс12`` may complete to
``арс12л``, but it must not match the different model ``арс125``.
"""
if query_code == candidate_code:
return True
query_match = _NORMALIZED_MODEL_RE.fullmatch(query_code)
candidate_match = _NORMALIZED_MODEL_RE.fullmatch(candidate_code)
if query_match is None or candidate_match is None:
return False
query_prefix, query_number, query_suffix = query_match.groups()
candidate_prefix, candidate_number, candidate_suffix = candidate_match.groups()
return (
query_prefix == candidate_prefix
and query_number == candidate_number
and not query_suffix
and bool(candidate_suffix)
)
def _detect_sds(normalized: str) -> str | None:
flattened = normalized.replace("-", " ")
if "sds max" in flattened or "сдс макс" in flattened:
@@ -645,7 +671,16 @@ def compare_attributes(
return conflict
if query.model_codes:
conflict = require(query.model_codes.issubset(candidate.model_codes), "model")
conflict = require(
all(
any(
model_code_matches(query_code, candidate_code)
for candidate_code in candidate.model_codes
)
for query_code in query.model_codes
),
"model",
)
if conflict:
return conflict
+22 -4
View File
@@ -10,6 +10,7 @@ from app.attributes import (
Compatibility,
ParsedAttributes,
compare_attributes,
model_code_matches,
parse_attributes,
)
from app.catalog import CatalogItem, build_catalog, load_raw_catalog
@@ -109,11 +110,15 @@ class CatalogMatcher:
product_types = {self.items[index].product_type for index in model_candidates}
if len(product_types) == 1:
product_type = next(iter(product_types))
exact_model = all(
code in self.model_index
for code in provisional_attributes.model_codes
)
product_match = ProductTypeMatch(
canonical=product_type,
score=1.0,
exact=True,
alias="model_code",
score=1.0 if exact_model else 0.93,
exact=exact_model,
alias="model_code" if exact_model else "model_code_prefix",
token_start=0,
)
if product_match is None and provisional_attributes.teeth is not None:
@@ -393,7 +398,20 @@ class CatalogMatcher:
def _model_candidates(self, model_codes: frozenset[str]) -> frozenset[int]:
if not model_codes:
return frozenset()
sets = [self.model_index.get(code, frozenset()) for code in model_codes]
sets: list[frozenset[int]] = []
for query_code in model_codes:
exact = self.model_index.get(query_code)
if exact:
sets.append(exact)
continue
partial_indexes: set[int] = set()
for candidate_code, indexes in self.model_index.items():
if model_code_matches(query_code, candidate_code):
partial_indexes.update(indexes)
sets.append(frozenset(partial_indexes))
if not sets or any(not indexes for indexes in sets):
return frozenset()
result = set(sets[0])