Files
tfm_ainventory/backend/ai/spare_parts_whitelist.py

164 lines
4.7 KiB
Python

"""
Spare-parts classification module for AI-powered item extraction.
This module provides functions to classify items as spare parts or consumables
using fuzzy matching against a predefined whitelist.
"""
from typing import Optional
from fuzzywuzzy import fuzz
SPARE_PART_CATEGORIES = [
"RAM", "DRAM", "DDR3", "DDR4", "DDR5", "SODIMM", "DIMM",
"SSD", "NVME", "M.2", "SATA", "HDD", "HARD DRIVE", "SOLID STATE DRIVE",
"CPU", "PROCESSOR", "APU", "GPU", "GRAPHICS CARD", "DISCRETE GPU",
"PSU", "POWER SUPPLY UNIT", "ADAPTER", "POWER MODULE",
"PCIE", "PCI", "RAID CONTROLLER", "NETWORK CARD", "NIC",
"HEATSINK", "CPU COOLER", "THERMAL SOLUTION",
"MOTHERBOARD", "BIOS", "CHIPSET"
]
CONSUMABLE_KEYWORDS = [
"CABLE", "CORD", "FASTENER", "SCREW", "WASHER", "BOLT", "STANDOFF",
"ADHESIVE", "THERMAL PASTE", "THERMAL PAD", "TAPE",
"CONNECTOR", "PLUG", "SOCKET", "ADAPTER"
]
POWER_SUPPLY_CONSUMABLE_KEYWORDS = [
"CABLE", "CORD", "GENERIC", "POWER CORD", "AC CORD"
]
# Regex patterns for matching common categories
MEMORY_PATTERNS = ["DDR", "DRAM", "RAM", "SODIMM", "DIMM"]
STORAGE_PATTERNS = ["SSD", "NVME", "SATA", "HDD", "M.2"]
PROCESSOR_PATTERNS = ["CPU", "GPU", "PROCESSOR", "CORE", "APU"]
PSU_PATTERNS = ["PSU", "POWER SUPPLY", "POWER UNIT"]
def classify_as_spare_part(category: str) -> bool:
"""
Classify an item as a spare part or consumable based on category string.
Uses a scoring algorithm combining exact matching, fuzzy matching, and
exclusion patterns to classify items.
Args:
category: Item category string (e.g., "Kingston DDR4 RAM", "6ft SATA Cable")
Returns:
True if item is classified as a spare part, False if consumable.
Examples:
>>> classify_as_spare_part("Kingston DDR4 RAM")
True
>>> classify_as_spare_part("6ft SATA Cable")
False
>>> classify_as_spare_part("CPU Mounting Hardware Kit")
False
>>> classify_as_spare_part("Corsair RM850x 850W PSU")
True
"""
if not category:
return False
# Normalize input
normalized = category.upper().strip()
# Check exact match in spare parts categories
for spare_part in SPARE_PART_CATEGORIES:
if spare_part == normalized:
return True
score = 0
# Check regex patterns for common categories
for pattern in MEMORY_PATTERNS:
if pattern in normalized:
score += 50
break
for pattern in STORAGE_PATTERNS:
if pattern in normalized:
score += 50
break
for pattern in PROCESSOR_PATTERNS:
if pattern in normalized:
score += 50
break
for pattern in PSU_PATTERNS:
if pattern in normalized:
score += 50
break
# Check fuzzy match against spare parts categories
best_fuzzy_score = 0
for spare_part in SPARE_PART_CATEGORIES:
fuzzy_score = fuzz.token_set_ratio(normalized, spare_part)
if fuzzy_score > best_fuzzy_score:
best_fuzzy_score = fuzzy_score
if best_fuzzy_score >= 80:
score += 50
elif best_fuzzy_score >= 70:
score += 30
# Check exclusion patterns (consumables) — override other scores
for keyword in CONSUMABLE_KEYWORDS:
if keyword in normalized:
score -= 100
# Special case: power supply is spare part, but power cable is consumable
if ("POWER SUPPLY" in normalized or "PSU" in normalized):
for consumable_keyword in POWER_SUPPLY_CONSUMABLE_KEYWORDS:
if consumable_keyword in normalized:
return False
# Final decision
return score >= 40
def is_consumable(category: str) -> bool:
"""
Determine if an item is a consumable (inverse of classify_as_spare_part).
Args:
category: Item category string
Returns:
True if item is a consumable, False if a spare part.
"""
return not classify_as_spare_part(category)
def get_spare_part_type(category: str) -> Optional[str]:
"""
Return the normalized spare-part type for a given category, or None if not a spare part.
Used for building web search queries with the specific part type.
Args:
category: Item category string
Returns:
Normalized spare-part type (e.g., "RAM", "SSD", "CPU") or None.
"""
if not classify_as_spare_part(category):
return None
normalized = category.upper().strip()
# Try to find best matching spare part type
best_match = None
best_score = 0
for spare_part in SPARE_PART_CATEGORIES:
fuzzy_score = fuzz.token_set_ratio(normalized, spare_part)
if fuzzy_score > best_score:
best_score = fuzzy_score
best_match = spare_part
return best_match if best_match else None