fix: 修复独立站日历节日去重与中文资料链接
This commit is contained in:
@@ -109,6 +109,7 @@ LOCAL_HOLIDAY_NAMES: dict[str, dict[str, tuple[str, str]]] = {
|
||||
"AU": ("Boxing Day (observed)", "英语"), "GB": ("Boxing Day (observed)", "英语"),
|
||||
},
|
||||
"lunar-new-year": {"CN": ("春节", "简体中文"), "KR": ("설날", "韩语"), "VN": ("Tết Nguyên Đán", "越南语"), "SG": ("农历新年 / Chinese New Year", "简体中文 / 英语")},
|
||||
"new-year-eve": {"DK": ("Nytårsaften", "丹麦语"), "SE": ("Nyårsafton", "瑞典语"), "PH": ("一年中的最后一天", "英语 / 菲律宾语")},
|
||||
"eid-fitr": {"AE": ("عيد الفطر", "阿拉伯语"), "TR": ("Ramazan Bayramı", "土耳其语"), "ID": ("Idul Fitri", "印度尼西亚语")},
|
||||
"eid-adha": {"AE": ("عيد الأضحى", "阿拉伯语"), "TR": ("Kurban Bayramı", "土耳其语"), "ID": ("Idul Adha", "印度尼西亚语")},
|
||||
"assumption-day": {
|
||||
@@ -118,6 +119,11 @@ LOCAL_HOLIDAY_NAMES: dict[str, dict[str, tuple[str, str]]] = {
|
||||
"PT": ("Assunção de Nossa Senhora", "葡萄牙语"), "ES": ("Asunción", "西班牙语"),
|
||||
},
|
||||
"indonesia-independence": {"ID": ("Hari Kemerdekaan Republik Indonesia", "印度尼西亚语")},
|
||||
"new-year": {
|
||||
"CN": ("元旦", "简体中文"), "JP": ("元日", "日语"), "RU": ("Новогодние каникулы", "俄语"),
|
||||
},
|
||||
"public-holiday:JP:coming of age day": {"JP": ("成人の日", "日语")},
|
||||
"public-holiday:JP:greenery day": {"JP": ("みどりの日", "日语")},
|
||||
}
|
||||
|
||||
FESTIVAL_TARGET_COUNTRIES = {
|
||||
@@ -139,6 +145,10 @@ HOLIDAY_TRANSLATIONS = {
|
||||
"whit monday": "圣灵降临节星期一", "pentecost monday": "圣灵降临节星期一",
|
||||
"king's birthday": "国王诞辰", "mountain day": "山之日", "marine day": "海之日",
|
||||
"respect for the aged day": "敬老日", "culture day": "文化日", "children's day": "儿童节",
|
||||
"new year s day": "元旦", "new year holiday": "新年假期",
|
||||
"new year s eve": "跨年夜", "last day of the year": "跨年夜",
|
||||
"coming of age day": "成人节", "greenery day": "绿之日",
|
||||
"labour thanksgiving day": "勤劳感谢之日", "labor thanksgiving day": "勤劳感谢之日",
|
||||
}
|
||||
|
||||
SUBDIVISION_NAMES = {
|
||||
@@ -282,6 +292,12 @@ FESTIVAL_PROFILES = {
|
||||
"design": ["使用新年焕新而非单一宗教表达", "按南北半球季节调整材质"],
|
||||
"avoid": ["南半球正值夏季,不要统一推保暖产品"],
|
||||
},
|
||||
"new-year-eve": {
|
||||
"title": "跨年夜", "relevance": "low", "decision": "仅用于运营排班",
|
||||
"what": "公历新年前夜或年末公共休假节点,主要影响跨年营业、物流和客服排班。",
|
||||
"scenes": ["跨年营业", "物流排班"], "products": [], "design": [],
|
||||
"avoid": ["不要把跨年夜与元旦或连续新年假期拆成多个重复消费节"],
|
||||
},
|
||||
"super-bowl": {
|
||||
"title": "超级碗观赛消费节点", "relevance": "high", "decision": "值得场景备货",
|
||||
"what": "美国职业橄榄球年度决赛,带动家庭观赛、聚会、酒店和城市活动消费。",
|
||||
@@ -371,9 +387,11 @@ FESTIVAL_PROFILES = {
|
||||
|
||||
GLOBAL_AGGREGATE_KEYS = {
|
||||
"black-friday", "cyber-monday", "winter-holiday", "christmas", "boxing-day",
|
||||
"lunar-new-year", "eid-fitr", "eid-adha", "easter", "assumption-day", "new-year", "st-stephens-day",
|
||||
"lunar-new-year", "eid-fitr", "eid-adha", "easter", "assumption-day", "new-year", "new-year-eve", "st-stephens-day",
|
||||
}
|
||||
|
||||
RANGE_AGGREGATE_KEYS = {"new-year", "eid-fitr", "eid-adha", "lunar-new-year"}
|
||||
|
||||
FESTIVAL_ENGLISH_TITLES = {
|
||||
"back-to-school": "Back to School",
|
||||
"black-friday": "Black Friday",
|
||||
@@ -385,6 +403,7 @@ FESTIVAL_ENGLISH_TITLES = {
|
||||
"eid-fitr": "Eid al-Fitr",
|
||||
"lunar-new-year": "Lunar New Year",
|
||||
"new-year": "New Year's Day",
|
||||
"new-year-eve": "New Year's Eve",
|
||||
"singles-day": "Singles' Day",
|
||||
"st-stephens-day": "St. Stephen's Day",
|
||||
"super-bowl": "Super Bowl",
|
||||
@@ -393,6 +412,7 @@ FESTIVAL_ENGLISH_TITLES = {
|
||||
}
|
||||
|
||||
GENERIC_HOLIDAY_TITLES = {"国家纪念与公共假日", "地区公共假日"}
|
||||
UNSAFE_CROSS_COUNTRY_TITLES = {"独立日", "国庆日", "共和国日", "宪法日", "解放日", "胜利日", "纪念日"}
|
||||
HOLIDAY_TITLE_ALIASES = {"五一劳动节": "劳动节"}
|
||||
|
||||
|
||||
@@ -446,7 +466,9 @@ def festival_key(name: str, source_id: str = "", country_code: str = "") -> str:
|
||||
return "eid-adha"
|
||||
if any(token in lower for token in ("easter", "good friday", "pentecost", "whit monday", "复活节", "耶稣受难")):
|
||||
return "easter"
|
||||
if any(token in lower for token in ("new year's day", "new year holiday", "元旦", "新年假期")):
|
||||
if any(token in lower for token in ("new year s eve", "last day of the year", "跨年夜")):
|
||||
return "new-year-eve"
|
||||
if any(token in lower for token in ("new year s day", "new year holiday", "new year s holiday", "元旦", "新年假期")):
|
||||
return "new-year"
|
||||
if any(token in lower for token in ("memorial", "remembrance", "all souls", "追悼", "纪念")):
|
||||
return "memorial"
|
||||
@@ -477,7 +499,12 @@ def calendar_event_type(name: str, source_id: str = "") -> str:
|
||||
|
||||
|
||||
def holiday_title(local_name: str, english_name: str, country_code: str) -> str:
|
||||
translated = HOLIDAY_TRANSLATIONS.get(english_name.lower())
|
||||
normalized = _normalized_name(english_name)
|
||||
if country_code == "ID" and normalized == "independence day":
|
||||
return "印度尼西亚独立日"
|
||||
if country_code == "PH" and normalized == "national heroes day":
|
||||
return "菲律宾国家英雄日"
|
||||
translated = HOLIDAY_TRANSLATIONS.get(english_name.lower()) or HOLIDAY_TRANSLATIONS.get(_normalized_name(english_name))
|
||||
if translated:
|
||||
return translated
|
||||
if country_code in ("CN", "JP", "KR") and local_name:
|
||||
@@ -650,17 +677,20 @@ def culture_reference_url(title: str) -> str:
|
||||
return f"https://zh.wikipedia.org/wiki/{quote(title.replace(' ', '_'), safe='')}"
|
||||
|
||||
|
||||
def culture_reference_cn_url(title: str) -> str:
|
||||
def culture_reference_cn_url(title: str, festival_key: str = "") -> str:
|
||||
"""中国大陆访客使用国内网络更易访问的百度百科。"""
|
||||
return f"https://baike.baidu.com/item/{quote(title, safe='')}"
|
||||
aliases = {"new-year": "元旦"}
|
||||
return f"https://baike.baidu.com/item/{quote(aliases.get(festival_key, title), safe='')}"
|
||||
|
||||
|
||||
def visual_reference_url(title: str, country_details: list[dict[str, Any]]) -> str:
|
||||
def visual_reference_url(title: str, country_details: list[dict[str, Any]], title_english: str = "") -> str:
|
||||
"""图片入口只搜索节日本身,不复用来源页标题。"""
|
||||
terms = [title]
|
||||
for detail in country_details[:2]:
|
||||
terms.extend((str(detail.get("local_name") or ""), str(detail.get("country") or "")))
|
||||
query = " ".join(_dedupe(terms))
|
||||
detail = next((row for row in country_details if row.get("country")), {})
|
||||
query_title = title_english.strip() or str(detail.get("local_name") or title).strip()
|
||||
if query_title in GENERIC_HOLIDAY_TITLES or not query_title:
|
||||
query_title = str(detail.get("local_name") or title).strip()
|
||||
country = str(detail.get("country_english") or detail.get("country") or "").strip()
|
||||
query = " ".join(_dedupe([query_title, country, "holiday"]))[:120]
|
||||
return f"https://www.bing.com/images/search?q={quote(query, safe='')}&setlang=zh-hans"
|
||||
|
||||
|
||||
@@ -668,7 +698,11 @@ def calendar_group_identity(item: dict[str, Any]) -> tuple[str, str]:
|
||||
"""同日同名的明确节日跨国家聚合;国家纪念类泛称仍按国家拆分。"""
|
||||
key = str(item.get("festival_key") or "public-holiday")
|
||||
title = HOLIDAY_TITLE_ALIASES.get(str(item.get("title") or "").strip(), str(item.get("title") or "").strip())
|
||||
if title and title not in GENERIC_HOLIDAY_TITLES and (key.startswith("public-holiday:") or key == "memorial"):
|
||||
if title and title not in GENERIC_HOLIDAY_TITLES and (
|
||||
key.startswith("public-holiday:")
|
||||
or key == "memorial"
|
||||
or (key.startswith("national:") and title not in UNSAFE_CROSS_COUNTRY_TITLES)
|
||||
):
|
||||
return f"named-holiday:{_normalized_name(title)}", ""
|
||||
country_partition = "" if key in GLOBAL_AGGREGATE_KEYS else str(item.get("country_code") or item.get("market_area") or item.get("region") or "")
|
||||
return key, country_partition
|
||||
@@ -677,6 +711,8 @@ def calendar_group_identity(item: dict[str, Any]) -> tuple[str, str]:
|
||||
def calendar_group_date(item: dict[str, Any]) -> str:
|
||||
"""圣诞季共用一张决策卡,其他节日仍按实际日期聚合。"""
|
||||
start_date = str(item["start_date"])
|
||||
if item.get("festival_key") in RANGE_AGGREGATE_KEYS:
|
||||
return f"@{start_date[:4]}"
|
||||
if item.get("festival_key") != "christmas":
|
||||
return start_date
|
||||
value = date.fromisoformat(start_date)
|
||||
@@ -704,8 +740,15 @@ def aggregate_calendar_insights(items: list[dict[str, Any]], now: date, poster_f
|
||||
grouped.setdefault((calendar_group_date(item), group_key, country_partition), []).append(item)
|
||||
|
||||
insights: list[dict[str, Any]] = []
|
||||
for (start_date, group_key, _), rows in grouped.items():
|
||||
rows = sorted(rows, key=lambda row: (str(row.get("start_date")) != start_date, str(row.get("start_date"))))
|
||||
for (group_date, group_key, _), rows in grouped.items():
|
||||
range_group = group_date.startswith("@")
|
||||
start_date = min(str(row.get("start_date") or group_date) for row in rows) if range_group else group_date
|
||||
rows = sorted(rows, key=lambda row: (
|
||||
str(row.get("start_date")) != start_date,
|
||||
0 if str(row.get("country_code") or "") == "CN" else 1,
|
||||
str(row.get("country_code") or ""),
|
||||
str(row.get("start_date") or ""),
|
||||
))
|
||||
row_keys = _dedupe([str(row.get("festival_key") or "public-holiday") for row in rows])
|
||||
key = row_keys[0] if len(row_keys) == 1 else f"public-holiday:{group_key.removeprefix('named-holiday:')}"
|
||||
profile = profile_for_market(key, str(rows[0].get("country_code") or ""))
|
||||
@@ -715,7 +758,7 @@ def aggregate_calendar_insights(items: list[dict[str, Any]], now: date, poster_f
|
||||
platforms = _dedupe([value for row in rows for value in row.get("platforms") or []])
|
||||
titles = [str(row.get("title") or "") for row in rows if row.get("title")]
|
||||
title = str(profile.get("title") or Counter(titles).most_common(1)[0][0])
|
||||
if key.startswith("public-holiday:") or key in ("national", "memorial"):
|
||||
if key.startswith(("public-holiday:", "national:")) or key in ("national", "memorial"):
|
||||
title = Counter(titles).most_common(1)[0][0]
|
||||
english_titles = [str(row.get("title_english") or "").strip() for row in rows if row.get("title_english")]
|
||||
title_english = FESTIVAL_ENGLISH_TITLES.get(key, "")
|
||||
@@ -822,6 +865,9 @@ def aggregate_calendar_insights(items: list[dict[str, Any]], now: date, poster_f
|
||||
"label": christmas_date_label(value, start_date) if key == "christmas" else "地区日期",
|
||||
"countries": variant_countries,
|
||||
})
|
||||
end_date = start_date if key == "christmas" else max(str(row.get("end_date") or start_date) for row in rows)
|
||||
if key == "new-year" and "RU" in country_codes:
|
||||
end_date = max(end_date, f"{start_date[:4]}-01-08")
|
||||
insight = {
|
||||
"id": f"calendar-insight-{digest}",
|
||||
"section": "calendar",
|
||||
@@ -842,7 +888,7 @@ def aggregate_calendar_insights(items: list[dict[str, Any]], now: date, poster_f
|
||||
"change_status": "官方日期已核验",
|
||||
"confidence": "高" if official_sources else "中高",
|
||||
"start_date": start_date,
|
||||
"end_date": start_date if key == "christmas" else max(str(row.get("end_date") or start_date) for row in rows),
|
||||
"end_date": end_date,
|
||||
"related_dates": related_dates,
|
||||
"date_variants": date_variants,
|
||||
"date_variation_note": (
|
||||
@@ -876,9 +922,9 @@ def aggregate_calendar_insights(items: list[dict[str, Any]], now: date, poster_f
|
||||
"days_until": (date.fromisoformat(start_date) - now).days,
|
||||
"priority_score": max(int(row.get("priority_score") or 0) for row in rows) + min(12, len(countries)),
|
||||
"analysis_basis": "事实层:官方日期与适用国家;分析层:家纺经营场景推断",
|
||||
"visual_reference_url": visual_reference_url(title, country_details),
|
||||
"visual_reference_url": visual_reference_url(title, country_details, title_english),
|
||||
"culture_reference_url": culture_reference_url(title),
|
||||
"culture_reference_cn_url": culture_reference_cn_url(title),
|
||||
"culture_reference_cn_url": culture_reference_cn_url(title, key),
|
||||
}
|
||||
translation = {
|
||||
key: first[key]
|
||||
@@ -889,6 +935,17 @@ def aggregate_calendar_insights(items: list[dict[str, Any]], now: date, poster_f
|
||||
)
|
||||
if key in first
|
||||
}
|
||||
if "CN" in country_codes:
|
||||
translation.update({
|
||||
"source_language": "zh-CN",
|
||||
"needs_translation": False,
|
||||
"google_translation_supported": False,
|
||||
"translation_url": "",
|
||||
"translation_provider": "",
|
||||
"translation_fallback_url": "",
|
||||
"translation_fallback_provider": "",
|
||||
"translation_notice": "该节日包含中国市场,中文访客直接查看中文节日资料。",
|
||||
})
|
||||
insight.update(translation)
|
||||
insight["poster"] = poster_for(insight["id"], "calendar", insight["culture_key"])
|
||||
insights.append(insight)
|
||||
|
||||
Reference in New Issue
Block a user