105 lines
3.1 KiB
Python
105 lines
3.1 KiB
Python
from __future__ import annotations
|
|
|
|
import re
|
|
from collections.abc import Iterable
|
|
from pathlib import Path
|
|
|
|
|
|
CPU_VENDOR_CHOICES = (
|
|
("intel", "Intel"),
|
|
("amd", "AMD"),
|
|
)
|
|
|
|
CPU_FAMILY_CHOICES = (
|
|
("intel_i3", "Intel Core i3"),
|
|
("intel_i5", "Intel Core i5"),
|
|
("intel_i7", "Intel Core i7"),
|
|
("intel_i9", "Intel Core i9"),
|
|
("intel_ultra_5", "Intel Core Ultra 5"),
|
|
("intel_ultra_7", "Intel Core Ultra 7"),
|
|
("intel_ultra_9", "Intel Core Ultra 9"),
|
|
("amd_ryzen_3", "AMD Ryzen 3"),
|
|
("amd_ryzen_5", "AMD Ryzen 5"),
|
|
("amd_ryzen_7", "AMD Ryzen 7"),
|
|
("amd_ryzen_9", "AMD Ryzen 9"),
|
|
("amd_ryzen_ai", "AMD Ryzen AI"),
|
|
("amd_threadripper", "AMD Threadripper"),
|
|
("amd_fx", "AMD FX"),
|
|
)
|
|
|
|
|
|
def _load_catalog() -> tuple[str, ...]:
|
|
values = []
|
|
for line in Path(__file__).with_name("cpu_models.txt").read_text(encoding="utf-8").splitlines():
|
|
value = line.strip()
|
|
if not value:
|
|
continue
|
|
if value.startswith("Core Ultra"):
|
|
value = f"Intel {value}"
|
|
values.append(value)
|
|
return tuple(dict.fromkeys(values))
|
|
|
|
|
|
CPU_MODELS = _load_catalog()
|
|
|
|
|
|
def _tokens(value: str) -> list[str]:
|
|
return re.findall(r"[a-z]+\d*|\d+[a-z]*", value.casefold())
|
|
|
|
|
|
def _short_tokens(value: str) -> list[str]:
|
|
tokens = _tokens(value)
|
|
while tokens and tokens[0] in {"intel", "amd", "core"}:
|
|
tokens.pop(0)
|
|
return tokens
|
|
|
|
|
|
def _match_score(model: str, term: str, preferred: bool) -> int:
|
|
query_tokens = _tokens(term)
|
|
if not query_tokens:
|
|
return 100 if preferred else 1
|
|
|
|
model_tokens = _tokens(model)
|
|
short_tokens = _short_tokens(model)
|
|
query_text = " ".join(query_tokens)
|
|
model_text = " ".join(model_tokens)
|
|
short_text = " ".join(short_tokens)
|
|
compact_query = "".join(query_tokens)
|
|
compact_model = "".join(model_tokens)
|
|
compact_short = "".join(short_tokens)
|
|
|
|
score = 0
|
|
if query_text == model_text:
|
|
score = 12_000
|
|
elif query_text == short_text:
|
|
score = 11_000
|
|
elif compact_query in {compact_model, compact_short}:
|
|
score = 10_000
|
|
elif all(token in model_tokens for token in query_tokens):
|
|
score = 9_000
|
|
elif all(any(candidate.startswith(token) for candidate in model_tokens) for token in query_tokens):
|
|
score = 8_000
|
|
elif compact_query in compact_model:
|
|
score = 7_000
|
|
elif all(token in model_text for token in query_tokens):
|
|
score = 6_000
|
|
else:
|
|
return 0
|
|
|
|
if preferred:
|
|
score += 250
|
|
score -= min(len(model), 200)
|
|
return score
|
|
|
|
|
|
def suggest_cpus(term: str, existing_values: Iterable[str] = (), limit: int = 15) -> list[str]:
|
|
existing = {value.strip() for value in existing_values if value and value.strip()}
|
|
candidates = list(dict.fromkeys([*existing, *CPU_MODELS]))
|
|
ranked = [
|
|
(_match_score(model, term, model in existing), index, model)
|
|
for index, model in enumerate(candidates)
|
|
]
|
|
ranked = [item for item in ranked if item[0] > 0]
|
|
ranked.sort(key=lambda item: (-item[0], item[1], item[2].casefold()))
|
|
return [model for _, _, model in ranked[:limit]]
|