#!/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()