| from src.aspect_sentiment.semantic_mapper import mapper |
| from src.aspect_sentiment.normalized_features import NormalizedOutput |
| import re |
|
|
| def clean_budget(value: str) -> int: |
| """Safely extract integer budget from string.""" |
| digits = re.sub(r'[^\d]', '', str(value)) |
| return int(digits) if digits else 0 |
|
|
|
|
| def normalize_decision_stage(value: str) -> str: |
| name = value.replace(" ", "_").lower() |
| if name in {"converted", "ready_to_purchase", "near_purchase", "negotiation", "decision", "final"}: |
| return "decision" |
| if name in {"evaluating", "comparing_alternatives", "considering", "budget_discussion", "mid"}: |
| return "mid" |
| if name in {"purchase_delayed", "follow_up_required", "late"}: |
| return "late" |
| if name in {"exploring", "awareness", "curious", "early"}: |
| return "early" |
| return "mid" |
|
|
|
|
| def normalize_urgency(value: str) -> str: |
| name = value.replace(" ", "_").lower() |
| if any(term in name for term in ["today", "now", "immediate", "urgent", "high"]): |
| return "immediate" |
| if any(term in name for term in ["tomorrow", "week", "soon", "medium"]): |
| return "soon" |
| if any(term in name for term in ["month", "later", "low", "flexible"]): |
| return "flexible" |
| if any(term in name for term in ["no_rush", "browsing", "exploring"]): |
| return "no_rush" |
| return "flexible" |
|
|
|
|
| def normalize_price_sensitivity(value: str) -> str: |
| name = value.lower() |
| if any(term in name for term in ["high", "very", "expensive", "cheap", "discount", "low budget", "price sensitive"]): |
| return "high" |
| if any(term in name for term in ["low", "premium", "not important"]): |
| return "low" |
| return "medium" |
|
|
| def process_extractions(raw_features: list[dict]) -> NormalizedOutput: |
| """ |
| Takes the raw features from LLaMA 3 and converts them into the |
| normalized XGBoost categorical structure. |
| """ |
| output = NormalizedOutput() |
| |
| for f in raw_features: |
| name = str(f.get("name", "")).strip().lower() |
| label = str(f.get("label", "")).upper() |
| |
| if not name: |
| continue |
| |
| |
| if label == "BUDGET" or any(c in name for c in ['$', '₹', 'rs', 'rupees']): |
| |
| gpu = mapper.extract_gpu(name) |
| if gpu: |
| |
| output.use_cases.append("gaming") |
| continue |
| |
| val = clean_budget(name) |
| if val > 0: |
| output.budget = val |
| continue |
| |
| |
| if label == "URGENCY" or label == "URGENCY_LEVEL": |
| output.urgency = normalize_urgency(name) |
| continue |
| |
| |
| if label == "DECISION_STAGE": |
| output.decision_stage = normalize_decision_stage(name) |
| continue |
|
|
| if label in {"PRICE_SENSITIVITY", "OBJECTION_TYPE"}: |
| output.price_sensitivity = normalize_price_sensitivity(name) |
| continue |
| |
| |
| mapped_tuples = mapper.map_term(name) |
| |
| |
| if not mapped_tuples: |
| if label == "PRODUCT": output.products.append(name.replace(" ", "_")) |
| elif label == "BRAND": output.brands.append(name.replace(" ", "_")) |
| elif label == "USE_CASE": output.use_cases.append(name.replace(" ", "_")) |
| |
| for val, mapped_label in mapped_tuples: |
| if mapped_label == "PRODUCT" and val not in output.products: |
| output.products.append(val) |
| elif mapped_label == "BRAND" and val not in output.brands: |
| output.brands.append(val) |
| elif mapped_label == "USE_CASE" and val not in output.use_cases: |
| output.use_cases.append(val) |
| |
| return output |
|
|