Commit 20098dc6 authored by Vũ Hoàng Anh's avatar Vũ Hoàng Anh

feat: add di_hoc occasion mapping and reason text sanitization for kid products

parent 0e3f8b6a
......@@ -459,6 +459,7 @@
}
.occ-badge-hang_ngay { background: #eef1f8; color: #3b5998; }
.occ-badge-di_lam { background: #ecfdf5; color: #16a34a; }
.occ-badge-di_hoc { background: #e0e7ff; color: #4f46e5; }
.occ-badge-di_choi { background: #fef3c7; color: #d97706; }
.occ-badge-the_thao { background: #fce7f3; color: #db2777; }
.occ-badge-mac_nha { background: #f3e8ff; color: #7c3aed; }
......@@ -540,6 +541,14 @@
let currentSimilarItems = [];
let currentOutfits = [];
function isKidProduct() {
if (!currentProduct) return false;
const age = (currentProduct.age_by_product || "").toLowerCase();
const gender = (currentProduct.gender_by_product || "").toLowerCase();
const name = (currentProduct.product_name || "").toLowerCase();
return age === 'kid' || age === 'baby' || gender === 'be_trai' || gender === 'be_gai' || name.includes("trẻ em") || name.includes("bé trai") || name.includes("bé gái") || name.includes("kid");
}
// ═══ IMAGE URL FORMATTER ═══
function formatImageUrl(url) {
if (!url) return '';
......@@ -1101,7 +1110,12 @@
currentProduct = data.product;
window.currentProduct = currentProduct;
currentSimilarItems = currentProduct.parsed_similar_items || [];
currentOutfits = currentProduct.parsed_outfit_recommendations || [];
currentOutfits = (currentProduct.parsed_outfit_recommendations || []).map(o => {
if (o.occasion === 'di_lam' && isKidProduct()) {
o.occasion = 'di_hoc';
}
return o;
});
// Update sidebar state badges to match current database state
const hasSim = currentSimilarItems.length > 0;
......@@ -1158,7 +1172,12 @@
currentProduct = data.product;
window.currentProduct = currentProduct;
currentSimilarItems = currentProduct.parsed_similar_items || [];
currentOutfits = currentProduct.parsed_outfit_recommendations || [];
currentOutfits = (currentProduct.parsed_outfit_recommendations || []).map(o => {
if (o.occasion === 'di_lam' && isKidProduct()) {
o.occasion = 'di_hoc';
}
return o;
});
// Cập nhật trạng thái sidebar dựa trên dữ liệu mới của biến thể màu
const baseCode = currentProduct.magento_ref_code || currentProduct.internal_ref_code;
......@@ -1493,6 +1512,7 @@
const OCC_LABELS = {
'hang_ngay': 'Hàng ngày',
'di_lam': 'Đi làm',
'di_hoc': 'Đi học',
'di_choi': 'Đi chơi',
'di_tiec': 'Đi tiệc',
'the_thao': 'Thể thao',
......@@ -1624,6 +1644,7 @@
color_name: p.product_color_name || '',
match_product_name: p.product_name,
role: "bottom",
occasion: activeOccFilter || (isKidProduct() ? "di_hoc" : "hang_ngay"),
reason: `Phối cùng ${p.product_name} giúp tôn lên phong cách trẻ trung và thoải mái khi diện hàng ngày.`,
product_image_url_thumbnail: p.product_image_url_thumbnail
});
......@@ -1738,6 +1759,7 @@
color_name: item.color_name || '',
match_product_name: item.match_product_name,
role: item.role || 'bottom',
occasion: item.occasion || (isKidProduct() ? 'di_hoc' : 'hang_ngay'),
reason: item.reason || ''
}));
......
......@@ -2,6 +2,7 @@
"occasions": [
"hang_ngay",
"di_lam",
"di_hoc",
"di_choi",
"di_tiec",
"the_thao",
......@@ -11,6 +12,7 @@
"occasion_labels": {
"hang_ngay": "Hàng ngày",
"di_lam": "Đi làm công sở",
"di_hoc": "Đi học",
"di_choi": "Đi chơi / dạo phố",
"di_tiec": "Đi tiệc",
"the_thao": "Thể thao",
......@@ -31,6 +33,15 @@
"work",
"office"
],
"di_hoc": [
"đi học",
"trường học",
"lớp học",
"school",
"class",
"học tập",
"vui học"
],
"di_choi": [
"đi chơi",
"dạo phố",
......
......@@ -120,11 +120,19 @@ class StylistEngine:
anchor_cat = source.get("product_line", "")
gender = source.get("gender", "")
is_kid = (
source.get("age_group", "").lower() == "kid"
or source.get("gender", "").lower() in ("boy", "girl", "be_trai", "be_gai")
or any(k in source.get("name", "").lower() for k in ["trẻ em", "bé trai", "bé gái", "cho bé", "unisex trẻ em"])
)
# Fetch allowed mappings from DB
db_rules = self._fetch_db_rules(anchor_cat, gender)
allowed_by_occ = {}
for r in db_rules:
occ = r["occ"]
if is_kid and occ == "di_lam":
occ = "di_hoc"
tgt = r["target_cat"].lower()
if occ not in allowed_by_occ:
allowed_by_occ[occ] = set()
......@@ -159,6 +167,16 @@ class StylistEngine:
if score < rules.get("min_score", 35):
continue
reason = self._build_reason(source, target, occ, score)
if is_kid:
reason = (reason
.replace("đi làm công sở", "đi học")
.replace("đi làm", "đi học")
.replace("công sở", "học tập và vui chơi")
.replace("văn phòng", "trường học")
.replace("làm việc", "học tập")
.replace("lịch sự dịp đi làm", "lịch sự dịp đi học")
.replace("phù hợp dịp đi học công sở", "phù hợp dịp đi học")
)
buckets[occ][role].append({
"code": target["code"],
"name": target["name"],
......@@ -202,6 +220,12 @@ class StylistEngine:
anchor_cat = source.get("product_line", "")
gender = source.get("gender", "")
is_kid = (
source.get("age_group", "").lower() == "kid"
or source.get("gender", "").lower() in ("boy", "girl", "be_trai", "be_gai")
or any(k in source.get("name", "").lower() for k in ["trẻ em", "bé trai", "bé gái", "cho bé", "unisex trẻ em"])
)
# 1. Fetch Rules from DB: list of {occ, role, target_cat, ai_reason}
db_rules = self._fetch_db_rules(anchor_cat, gender)
if not db_rules:
......@@ -211,9 +235,12 @@ class StylistEngine:
# Group by occasion for easier lookup
rules_by_occ: dict[str, list[dict]] = {}
for r in db_rules:
if r["occ"] not in rules_by_occ:
rules_by_occ[r["occ"]] = []
rules_by_occ[r["occ"]].append(r)
occ = r["occ"]
if is_kid and occ == "di_lam":
occ = "di_hoc"
if occ not in rules_by_occ:
rules_by_occ[occ] = []
rules_by_occ[occ].append(r)
max_items = self.rules.get("role_max_items", {})
buckets: dict[str, dict[str, list[dict]]] = {}
......@@ -245,6 +272,18 @@ class StylistEngine:
db_reason = r["ai_reason"] or f"Theo định hướng ({occ})"
combined_reason = f"{algo_reason} | {db_reason}"
if is_kid:
# Sanitize reason text to replace work/office terms with school terms
combined_reason = (combined_reason
.replace("đi làm công sở", "đi học")
.replace("đi làm", "đi học")
.replace("công sở", "học tập và vui chơi")
.replace("văn phòng", "trường học")
.replace("làm việc", "học tập")
.replace("lịch sự dịp đi làm", "lịch sự dịp đi học")
.replace("phù hợp dịp đi học công sở", "phù hợp dịp đi học")
)
breakdown = self._score_breakdown(source, target, occ)
if occ not in buckets:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment