Files
2026-03-21 18:57:10 +08:00

321 lines
12 KiB
Python

#!/usr/bin/env python3
"""
Random PPT Theme Selector
If user doesn't select a PPT template, this script will randomly select one
from the available templates and generate PPT.
"""
import os
import sys
import json
import random
import argparse
import subprocess
import time
def get_available_themes():
"""Get available PPT themes"""
try:
api_key = os.getenv("BAIDU_API_KEY")
if not api_key:
print("Error: BAIDU_API_KEY environment variable not set", file=sys.stderr)
return []
# Import the function from ppt_theme_list.py
script_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, script_dir)
from ppt_theme_list import ppt_theme_list as get_themes
themes = get_themes(api_key)
return themes
except Exception as e:
print(f"Error getting themes: {e}", file=sys.stderr)
return []
def categorize_themes(themes):
"""Categorize themes by style for better random selection"""
categorized = {
"企业商务": [],
"文艺清新": [],
"卡通手绘": [],
"扁平简约": [],
"中国风": [],
"年终总结": [],
"创意趣味": [],
"文化艺术": [],
"未来科技": [],
"默认": []
}
for theme in themes:
style_names = theme.get("style_name_list", [])
if not style_names:
categorized["默认"].append(theme)
continue
added = False
for style_name in style_names:
if style_name in categorized:
categorized[style_name].append(theme)
added = True
break
if not added:
categorized["默认"].append(theme)
return categorized
def select_random_theme_by_category(categorized_themes, preferred_category=None):
"""Select a random theme, optionally preferring a specific category"""
# If preferred category specified and has themes, use it
if preferred_category and preferred_category in categorized_themes:
if categorized_themes[preferred_category]:
return random.choice(categorized_themes[preferred_category])
# Otherwise, select from all non-empty categories
available_categories = []
for category, themes in categorized_themes.items():
if themes:
available_categories.append(category)
if not available_categories:
return None
# Weighted random selection: prefer non-default categories
weights = []
for category in available_categories:
if category == "默认":
weights.append(0.5) # Lower weight for default
else:
weights.append(2.0) # Higher weight for specific styles
# Normalize weights
total_weight = sum(weights)
weights = [w/total_weight for w in weights]
selected_category = random.choices(available_categories, weights=weights, k=1)[0]
return random.choice(categorized_themes[selected_category])
def suggest_category_by_query(query):
"""Suggest template category based on query keywords - enhanced version"""
query_lower = query.lower()
# Comprehensive keyword mapping with priority order
keyword_mapping = [
# Business & Corporate (highest priority for formal content)
("企业商务", [
"企业", "公司", "商务", "商业", "商务", "商业计划", "商业报告",
"营销", "市场", "销售", "财务", "会计", "审计", "投资", "融资",
"战略", "管理", "运营", "人力资源", "hr", "董事会", "股东",
"年报", "季报", "财报", "业绩", "kpi", "okr", "商业计划书",
"提案", "策划", "方案", "报告", "总结", "规划", "计划"
]),
# Technology & Future Tech
("未来科技", [
"未来", "科技", "人工智能", "ai", "机器学习", "深度学习",
"大数据", "云计算", "区块链", "物联网", "iot", "5g", "6g",
"量子计算", "机器人", "自动化", "智能制造", "智慧城市",
"虚拟现实", "vr", "增强现实", "ar", "元宇宙", "数字孪生",
"芯片", "半导体", "集成电路", "电子", "通信", "网络",
"网络安全", "信息安全", "数字化", "数字化转型",
"科幻", "高科技", "前沿科技", "科技创新", "技术"
]),
# Education & Children
("卡通手绘", [
"卡通", "动画", "动漫", "儿童", "幼儿", "小学生", "中学生",
"教育", "教学", "课件", "教案", "学习", "培训", "教程",
"趣味", "有趣", "可爱", "活泼", "生动", "绘本", "漫画",
"手绘", "插画", "图画", "图形", "游戏", "玩乐", "娱乐"
]),
# Year-end & Summary
("年终总结", [
"年终", "年度", "季度", "月度", "周报", "日报",
"总结", "回顾", "汇报", "述职", "考核", "评估",
"成果", "成绩", "业绩", "绩效", "目标", "完成",
"工作汇报", "工作总结", "年度报告", "季度报告"
]),
# Minimalist & Modern Design
("扁平简约", [
"简约", "简洁", "简单", "极简", "现代", "当代",
"设计", "视觉", "ui", "ux", "用户体验", "用户界面",
"科技感", "数字感", "数据", "图表", "图形", "信息图",
"分析", "统计", "报表", "dashboard", "仪表板",
"互联网", "web", "移动", "app", "应用", "软件"
]),
# Chinese Traditional
("中国风", [
"中国", "中华", "传统", "古典", "古风", "古代",
"文化", "文明", "历史", "国学", "东方", "水墨",
"书法", "国画", "诗词", "古文", "经典", "传统节日",
"春节", "中秋", "端午", "节气", "风水", "易经",
"", "", "", "", "茶道", "瓷器", "丝绸"
]),
# Cultural & Artistic
("文化艺术", [
"文化", "艺术", "文艺", "美学", "审美", "创意",
"创作", "作品", "展览", "博物馆", "美术馆", "画廊",
"音乐", "舞蹈", "戏剧", "戏曲", "电影", "影视",
"摄影", "绘画", "雕塑", "建筑", "设计", "时尚",
"文学", "诗歌", "小说", "散文", "哲学", "思想"
]),
# Artistic & Fresh
("文艺清新", [
"文艺", "清新", "小清新", "治愈", "温暖", "温柔",
"浪漫", "唯美", "优雅", "精致", "细腻", "柔和",
"自然", "生态", "环保", "绿色", "植物", "花卉",
"风景", "旅行", "游记", "生活", "日常", "情感"
]),
# Creative & Fun
("创意趣味", [
"创意", "创新", "创造", "发明", "新奇", "新颖",
"独特", "个性", "特色", "趣味", "有趣", "好玩",
"幽默", "搞笑", "笑话", "娱乐", "休闲", "放松",
"脑洞", "想象力", "灵感", "点子", "想法", "概念"
]),
# Academic & Research
("默认", [
"研究", "学术", "科学", "论文", "课题", "项目",
"实验", "调查", "分析", "理论", "方法", "技术",
"医学", "健康", "医疗", "生物", "化学", "物理",
"数学", "工程", "建筑", "法律", "政治", "经济",
"社会", "心理", "教育", "学习", "知识", "信息"
])
]
# Check each category with its keywords
for category, keywords in keyword_mapping:
for keyword in keywords:
if keyword in query_lower:
return category
# If no match found, analyze query length and content
words = query_lower.split()
if len(words) <= 3:
# Short query, likely specific - use "默认" or tech-related
if any(word in query_lower for word in ["ai", "vr", "ar", "iot", "5g", "tech"]):
return "未来科技"
return "默认"
else:
# Longer query, analyze word frequency
word_counts = {}
for word in words:
if len(word) > 1: # Ignore single characters
word_counts[word] = word_counts.get(word, 0) + 1
# Check for business indicators
business_words = ["报告", "总结", "计划", "方案", "业绩", "销售", "市场"]
if any(word in word_counts for word in business_words):
return "企业商务"
# Check for tech indicators
tech_words = ["技术", "科技", "数据", "数字", "智能", "系统"]
if any(word in word_counts for word in tech_words):
return "未来科技"
# Default fallback
return "默认"
def generate_ppt_with_random_theme(query, preferred_category=None):
"""Generate PPT with randomly selected theme"""
# Get available themes
themes = get_available_themes()
if not themes:
print("Error: No available themes found", file=sys.stderr)
return False
# Categorize themes
categorized = categorize_themes(themes)
# Select random theme
selected_theme = select_random_theme_by_category(categorized, preferred_category)
if not selected_theme:
print("Error: Could not select a theme", file=sys.stderr)
return False
style_id = selected_theme.get("style_id", 0)
tpl_id = selected_theme.get("tpl_id")
style_names = selected_theme.get("style_name_list", ["默认"])
print(f"Selected template: {style_names[0]} (tpl_id: {tpl_id})", file=sys.stderr)
# Generate PPT
script_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "generate_ppt.py")
try:
# Run generate_ppt.py with the selected theme
cmd = [
sys.executable, script_path,
"--query", query,
"--tpl_id", str(tpl_id),
"--style_id", str(style_id)
]
start_time = int(time.time())
process = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
bufsize=1,
universal_newlines=True
)
# Stream output
for line in process.stdout:
line = line.strip()
if line:
try:
data = json.loads(line)
if "is_end" in data and data["is_end"]:
print(json.dumps(data, ensure_ascii=False))
else:
end_time = int(time.time())
print(json.dumps({"status": data.get("status", "生成中"), "run_time": end_time - start_time}, ensure_ascii=False))
except json.JSONDecodeError:
# Just print non-JSON output
print(line)
process.wait()
return process.returncode == 0
except Exception as e:
print(f"Error generating PPT: {e}", file=sys.stderr)
return False
def main():
parser = argparse.ArgumentParser(description="Generate PPT with random theme selection")
parser.add_argument("--query", "-q", type=str, required=True, help="PPT主题/内容")
parser.add_argument("--category", "-c", type=str, help="Preferred category (企业商务/文艺清新/卡通手绘/扁平简约/中国风/年终总结/创意趣味/文化艺术/未来科技)")
args = parser.parse_args()
# Determine preferred category
preferred_category = args.category
if not preferred_category:
preferred_category = suggest_category_by_query(args.query)
if preferred_category:
print(f"Auto-suggested category: {preferred_category}", file=sys.stderr)
# Generate PPT
success = generate_ppt_with_random_theme(args.query, preferred_category)
if not success:
sys.exit(1)
if __name__ == "__main__":
main()