- 缓存热点批量流程(有采集缓存不触发 Google) - 简报不足直接从采集缓存生成(轻量补齐) - 三图合成(模特/印花/底图)+ 底图压缩 <2MB - 热点去重→风格去重自动切换 + 不适合类目 review 兜底 - 透明背景(background=transparent)+ 提示词清洗(敏感词/背景描述) - 任务前 basemap 校验 + 模板国家校验 + 模特任务级分配
187 lines
7.1 KiB
Python
187 lines
7.1 KiB
Python
"""月份 / 季节 / 临近节日上下文(按国家),供动态种子词生成的 LLM 上下文使用。
|
||
|
||
提供 build_holiday_context(country, now=None) -> dict:
|
||
{
|
||
"date": "2026-08-21",
|
||
"year": 2026,
|
||
"month": 8,
|
||
"season": "Summer",
|
||
"month_themes": ["back to school", "late summer", "outdoor adventure"],
|
||
"upcoming_holidays": ["Back to School", "Summer Solstice"]
|
||
}
|
||
- month_themes:固定月度灵感,作为种子词生成的稳定基础。
|
||
- upcoming_holidays:按国家节日表,用窗口计算临近(含刚过的)固定/浮动节日,
|
||
让 LLM 注入"当前该国的可能节日",生成对应节日主题种子。
|
||
"""
|
||
import datetime
|
||
from typing import Dict, List, Optional
|
||
|
||
# 北半球季节(南半球可反向扩展)
|
||
SEASON_BY_MONTH = {
|
||
12: "Winter", 1: "Winter", 2: "Winter",
|
||
3: "Spring", 4: "Spring", 5: "Spring",
|
||
6: "Summer", 7: "Summer", 8: "Summer",
|
||
9: "Autumn", 10: "Autumn", 11: "Autumn",
|
||
}
|
||
|
||
# 月度主题词(通用印花设计灵感)
|
||
MONTH_THEMES: Dict[int, List[str]] = {
|
||
1: ["new year", "winter cozy", "resolution"],
|
||
2: ["valentine", "love", "heart"],
|
||
3: ["spring bloom", "st patrick", "fresh start"],
|
||
4: ["easter", "spring garden", "pastel"],
|
||
5: ["mother day", "flower", "spring outdoor"],
|
||
6: ["pride", "summer start", "beach"],
|
||
7: ["summer vibe", "travel", "festival"],
|
||
8: ["back to school", "late summer", "outdoor adventure"],
|
||
9: ["autumn equinox", "harvest", "cozy"],
|
||
10: ["halloween", "autumn goth", "spooky"],
|
||
11: ["thanksgiving", "gratitude", "autumn warm"],
|
||
12: ["christmas", "winter holiday", "cozy festive"],
|
||
}
|
||
|
||
# (name, month, day, rule, window_days)
|
||
# rule: None=固定日; "mother"=第2周日; "father"=第3周日; "thanks"=第4周四; "easter"=Computus; "bf"=thanks+1
|
||
_HOLIDAYS_BY_COUNTRY: Dict[str, List[tuple]] = {
|
||
"US": [
|
||
("New Year", 1, 1, None, 14),
|
||
("Valentine's Day", 2, 14, None, 21),
|
||
("St Patrick's Day", 3, 17, None, 14),
|
||
("Easter", 0, 0, "easter", 21),
|
||
("Mother's Day", 5, 0, "mother", 14),
|
||
("Father's Day", 6, 0, "father", 14),
|
||
("Pride Month", 6, 1, None, 7),
|
||
("Independence Day", 7, 4, None, 21),
|
||
("Summer Solstice", 6, 21, None, 14),
|
||
("Back to School", 8, 15, None, 30),
|
||
("Labor Day", 9, 0, "labor", 14),
|
||
("Halloween", 10, 31, None, 30),
|
||
("Thanksgiving (US)", 11, 0, "thanks", 21),
|
||
("Black Friday", 11, 0, "bf", 14),
|
||
("Christmas", 12, 25, None, 30),
|
||
("Winter Solstice", 12, 21, None, 14),
|
||
],
|
||
"GB": [
|
||
("New Year", 1, 1, None, 14),
|
||
("Valentine's Day", 2, 14, None, 21),
|
||
("St Patrick's Day", 3, 17, None, 14),
|
||
("Easter", 0, 0, "easter", 21),
|
||
("Mother's Day (UK)", 3, 0, "mother_uk", 14),
|
||
("Father's Day", 6, 0, "father", 14),
|
||
("Summer Bank Holiday", 8, 25, None, 14),
|
||
("Halloween", 10, 31, None, 30),
|
||
("Bonfire Night", 11, 5, None, 21),
|
||
("Remembrance Day", 11, 11, None, 14),
|
||
("Christmas", 12, 25, None, 30),
|
||
("Boxing Day", 12, 26, None, 14),
|
||
],
|
||
"JP": [
|
||
("New Year", 1, 1, None, 14),
|
||
("Valentine's Day", 2, 14, None, 21),
|
||
("Hinamatsuri", 3, 3, None, 14), # 雏祭
|
||
("Hanami", 4, 1, None, 21), # 花见(樱花季)
|
||
("Golden Week", 4, 29, None, 21),
|
||
("Children's Day", 5, 5, None, 14), # 子供の日
|
||
("Tanabata", 7, 7, None, 14), # 七夕
|
||
("Fireworks Season", 8, 1, None, 30), # 花火大会
|
||
("Obon", 8, 13, None, 21), # お盆
|
||
("Halloween", 10, 31, None, 30),
|
||
("Christmas", 12, 25, None, 30),
|
||
("New Year Eve", 12, 31, None, 14), # 大晦日
|
||
],
|
||
"AU": [
|
||
("New Year", 1, 1, None, 14),
|
||
("Australia Day", 1, 26, None, 21),
|
||
("Valentine's Day", 2, 14, None, 21),
|
||
("Easter", 0, 0, "easter", 21),
|
||
("Anzac Day", 4, 25, None, 21),
|
||
("Mother's Day (AU)", 5, 0, "mother", 14),
|
||
("Father's Day (AU)", 9, 0, "father", 14),
|
||
("Summer Christmas", 12, 25, None, 30),
|
||
("Boxing Day", 12, 26, None, 21),
|
||
("Halloween", 10, 31, None, 21),
|
||
],
|
||
}
|
||
|
||
|
||
def _easter(year: int) -> datetime.date:
|
||
a = year % 19
|
||
b = year // 100
|
||
c = year % 100
|
||
d = b // 4
|
||
e = b % 4
|
||
f = (b + 8) // 25
|
||
g = (b - f + 1) // 3
|
||
h = (19 * a + b - d - g + 15) % 30
|
||
i = c // 4
|
||
k = c % 4
|
||
l = (32 + 2 * e + 2 * i - h - k) % 7
|
||
m = (a + 11 * h + 22 * l) // 451
|
||
month = (h + l - 7 * m + 114) // 31
|
||
day = ((h + l - 7 * m + 114) % 31) + 1
|
||
return datetime.date(year, month, day)
|
||
|
||
|
||
def _resolve(name: str, month: int, day: int, rule, year: int) -> Optional[datetime.date]:
|
||
if rule is None:
|
||
return datetime.date(year, month, day)
|
||
if rule == "easter":
|
||
return _easter(year)
|
||
if rule in ("mother", "mother_uk"):
|
||
# 第2个周日(UK 用"母亲节"但实际 3 月第4周日前的第4大斋期周日——简化为 3 月第2周日)
|
||
if rule == "mother_uk":
|
||
month, week = 3, 2
|
||
else:
|
||
month, week = month, 2
|
||
first = datetime.date(year, month, 1)
|
||
return first + datetime.timedelta(days=(6 - first.weekday()) % 7 + (week - 1) * 7)
|
||
if rule == "father":
|
||
first = datetime.date(year, month, 1)
|
||
return first + datetime.timedelta(days=(6 - first.weekday()) % 7 + 2 * 7)
|
||
if rule == "thanks":
|
||
first = datetime.date(year, month, 1)
|
||
return first + datetime.timedelta(days=(3 - first.weekday()) % 7 + 3 * 7)
|
||
if rule == "bf":
|
||
t = _resolve("", 11, 0, "thanks", year)
|
||
return t + datetime.timedelta(days=1) if t else None
|
||
if rule == "labor":
|
||
first = datetime.date(year, month, 1)
|
||
return first + datetime.timedelta(days=(0 - first.weekday()) % 7)
|
||
return None
|
||
|
||
|
||
def holidays_for(country: str = "US") -> List[tuple]:
|
||
key = (country or "US").upper()
|
||
return _HOLIDAYS_BY_COUNTRY.get(key, _HOLIDAYS_BY_COUNTRY["US"])
|
||
|
||
|
||
def upcoming_holidays(now: Optional[datetime.date] = None, country: str = "US",
|
||
lower: int = -10) -> List[str]:
|
||
"""返回该国临近(未来 window 内,或刚过 lower 天内)的节日名。"""
|
||
now = now or datetime.date.today()
|
||
out: List[str] = []
|
||
for name, month, day, rule, window in holidays_for(country):
|
||
try:
|
||
d = _resolve(name, month, day, rule, now.year)
|
||
except Exception:
|
||
continue
|
||
if d is None:
|
||
continue
|
||
delta = (d - now).days
|
||
if lower <= delta <= window:
|
||
out.append(name)
|
||
return out
|
||
|
||
|
||
def build_holiday_context(country: str = "US",
|
||
now: Optional[datetime.date] = None) -> Dict[str, object]:
|
||
now = now or datetime.date.today()
|
||
return {
|
||
"date": now.isoformat(),
|
||
"year": now.year,
|
||
"month": now.month,
|
||
"season": SEASON_BY_MONTH.get(now.month, ""),
|
||
"month_themes": MONTH_THEMES.get(now.month, []),
|
||
"upcoming_holidays": upcoming_holidays(now, country),
|
||
}
|