diff --git a/data.py b/data.py new file mode 100644 index 0000000..ec828b0 --- /dev/null +++ b/data.py @@ -0,0 +1,232 @@ +# 英语学习数据(Python版本) + +english_data = { + "scenes": [ + { + "id": 0, + "title": "餐厅点餐", + "description": "在餐厅点餐的日常对话练习", + "image": "", + "conversation": [ + { + "speaker": "服务员", + "text": "Good afternoon! Welcome to our restaurant. May I take your order?", + "type": "other" + }, + { + "speaker": "我", + "text": "I'd like to see the menu first, please.", + "type": "user" + }, + { + "speaker": "服务员", + "text": "Sure, here you are. Take your time.", + "type": "other" + }, + { + "speaker": "服务员", + "text": "Are you ready to order now?", + "type": "other" + }, + { + "speaker": "我", + "question": "请选择合适的回答:", + "options": [ + "Yes, I'll have the steak, please.", + "No, I don't like this restaurant.", + "I want to go home.", + "The menu is too expensive." + ], + "correctAnswer": 0, + "type": "user_question" + }, + { + "speaker": "服务员", + "text": "How would you like your steak cooked?", + "type": "other" + }, + { + "speaker": "我", + "question": "请选择合适的回答:", + "options": [ + "Medium rare, please.", + "With fries and salad.", + "I want a drink.", + "That's all." + ], + "correctAnswer": 0, + "type": "user_question" + }, + { + "speaker": "服务员", + "text": "Would you like anything to drink?", + "type": "other" + }, + { + "speaker": "我", + "question": "请选择合适的回答:", + "options": [ + "A glass of water, please.", + "No, I'm not hungry.", + "I'll pay now.", + "Thank you." + ], + "correctAnswer": 0, + "type": "user_question" + }, + { + "speaker": "服务员", + "text": "Okay, your order will be ready in 15 minutes.", + "type": "other" + }, + { + "speaker": "我", + "question": "请选择合适的回答:", + "options": [ + "Thank you very much.", + "Hurry up!", + "I want it now.", + "That's too long." + ], + "correctAnswer": 0, + "type": "user_question" + } + ] + }, + { + "id": 1, + "title": "超市购物", + "description": "在超市购物的日常对话练习", + "image": "", + "conversation": [ + { + "speaker": "顾客", + "text": "Excuse me, where can I find the milk?", + "type": "user" + }, + { + "speaker": "超市员工", + "text": "It's in aisle 5, next to the bread.", + "type": "other" + }, + { + "speaker": "顾客", + "question": "请选择合适的回答:", + "options": [ + "Thank you very much.", + "I don't like milk.", + "Where is aisle 5?", + "This store is too big." + ], + "correctAnswer": 0, + "type": "user_question" + }, + { + "speaker": "超市员工", + "text": "You're welcome. Let me know if you need anything else.", + "type": "other" + }, + { + "speaker": "顾客", + "text": "Actually, do you have any organic vegetables?", + "type": "user" + }, + { + "speaker": "超市员工", + "text": "Yes, they're in the fresh produce section at the back of the store.", + "type": "other" + }, + { + "speaker": "顾客", + "question": "请选择合适的回答:", + "options": [ + "Great, thank you.", + "I don't want organic.", + "That's too far.", + "Why are they there?" + ], + "correctAnswer": 0, + "type": "user_question" + } + ] + }, + { + "id": 2, + "title": "问路", + "description": "向陌生人问路的日常对话练习", + "image": "", + "conversation": [ + { + "speaker": "我", + "text": "Excuse me, could you tell me how to get to the nearest subway station?", + "type": "user" + }, + { + "speaker": "路人", + "text": "Sure! Go straight for two blocks, then turn left at the traffic light. You'll see it on your right.", + "type": "other" + }, + { + "speaker": "我", + "question": "请选择合适的回答:", + "options": [ + "Is it far from here?", + "I don't want to go there.", + "That's too complicated.", + "I'll take a taxi instead." + ], + "correctAnswer": 0, + "type": "user_question" + }, + { + "speaker": "路人", + "text": "No, it's only about a 5-minute walk.", + "type": "other" + } + ] + }, + { + "id": 5, + "title": "机场值机", + "description": "在机场办理值机手续的日常对话练习", + "image": "", + "conversation": [ + { + "speaker": "值机人员", + "text": "Good morning! How can I help you today?", + "type": "other" + }, + { + "speaker": "我", + "text": "I need to check in for my flight, please.", + "type": "user" + }, + { + "speaker": "值机人员", + "text": "Certainly! What's your flight number and destination?", + "type": "other" + }, + { + "speaker": "我", + "question": "请输入您的航班号和目的地(例如:CA1234 to Beijing):", + "type": "user_question" + }, + { + "speaker": "值机人员", + "text": "Thank you! Do you have any baggage to check?", + "type": "other" + }, + { + "speaker": "我", + "question": "请回答是否有行李要托运(例如:Yes, one suitcase please. 或 No, just carry-on.):", + "type": "user_question" + }, + { + "speaker": "值机人员", + "text": "Perfect! Here's your boarding pass...", + "type": "other" + } + ] + } + ] +} diff --git a/html_python_guide.md b/html_python_guide.md new file mode 100644 index 0000000..476653b --- /dev/null +++ b/html_python_guide.md @@ -0,0 +1,66 @@ +# HTML与Python集成指南 + +## 技术限制说明 + +HTML文件**不能直接引用或执行Python文件**,这是因为: + +1. **运行环境不同**: + - HTML/JavaScript 运行在浏览器端(客户端) + - Python 运行在服务器端或本地命令行环境 + +2. **执行方式不同**: + - JavaScript 是解释型语言,浏览器内置了JavaScript引擎 + - Python 需要安装Python解释器才能执行 + +3. **语法和API完全不同**: + - HTML只能识别并加载JavaScript文件(通过` + + + + + print("Hello from Python in HTML!") + # 可以导入Python模块和使用Python语法 + + + +``` + +### 方案2:创建Web应用(高级) + +使用Python Web框架(如Flask、Django)创建完整的Web应用: + +1. **服务器端**:使用Python处理业务逻辑和API调用 +2. **客户端**:使用HTML/JavaScript构建用户界面 +3. **通信**:通过AJAX/API实现前后端数据交互 + +### 方案3:保留现有两个版本 + +- **网页版**:继续使用index.html + data.js + script.js +- **命令行版**:使用Python版(main.py + data.py + script.py) + +## 建议 + +如果您希望: +- **快速使用**:保留两个版本,分别使用不同的方式运行 +- **网页界面+Python功能**:尝试PyScript方案 +- **完整Web应用**:学习并使用Python Web框架 + +请根据您的需求选择合适的方案。 \ No newline at end of file diff --git a/main.py b/main.py index 30c69ab..1b33241 100644 --- a/main.py +++ b/main.py @@ -1,5 +1,32 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +英语学习程序入口文件 +""" + +from script import EnglishLearningApp + def main(): - print("Hello from english-learning-uv!") + """主函数""" + print("英语学习程序") + print("=" * 50) + print("这是一个交互式的英语对话练习程序") + print("通过模拟日常场景,帮助您提高英语对话能力") + print("=" * 50) + + try: + # 创建应用实例 + app = EnglishLearningApp() + + # 启动应用 + app.start() + + except KeyboardInterrupt: + print("\n\n程序已中断") + print("感谢使用英语学习程序!") + except Exception as e: + print(f"\n\n程序发生错误: {e}") + print("请检查配置或联系技术支持") if __name__ == "__main__": diff --git a/script.py b/script.py new file mode 100644 index 0000000..e85ba06 --- /dev/null +++ b/script.py @@ -0,0 +1,329 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +英语学习程序主脚本(Python版本) +实现对话流程、API集成和用户交互功能 +""" + +import json +import time +import requests +import os +from typing import Dict, List, Optional + +# 导入数据 +from data import english_data + +class GameState: + """游戏状态管理类""" + def __init__(self): + self.current_scene_index = 0 + self.current_dialog_index = 0 + self.score = 0 + self.total_questions = 0 + self.correct_answers = 0 + + def reset(self): + """重置游戏状态""" + self.current_scene_index = 0 + self.current_dialog_index = 0 + self.score = 0 + self.total_questions = 0 + self.correct_answers = 0 + +class EnglishLearningApp: + """英语学习应用主类""" + def __init__(self): + self.game_state = GameState() + self.current_scene = None + self.api_key = os.getenv("DEEPSEEK_API_KEY", "your-api-key-here") + self.api_url = "https://api.deepseek.com/v1/chat/completions" + + def load_scene(self, scene_index: int) -> bool: + """加载指定场景""" + if scene_index < 0 or scene_index >= len(english_data["scenes"]): + print("所有场景已完成!") + return False + + self.current_scene = english_data["scenes"][scene_index] + self.game_state.current_scene_index = scene_index + self.game_state.current_dialog_index = 0 + + print("\n" + "=" * 50) + print(f"场景:{self.current_scene['title']}") + print(f"描述:{self.current_scene['description']}") + print("=" * 50) + + # 加载场景图片(在命令行环境下仅显示图片路径) + self._load_scene_image() + + # 开始对话 + self.show_next_dialog() + return True + + def _load_scene_image(self): + """加载场景图片""" + scene = self.current_scene + image_path = scene.get("image", "") + + if not image_path: + # 自动查找图片 + possible_extensions = [".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".svg"] + base_name_id = str(scene["id"]) + + # 简单的拼音转换(仅保留字母数字) + base_name_title = ''.join(c for c in scene["title"] if c.isalnum()).lower() + + all_possible_paths = [ + # 优先使用ID命名 + *[f"images/{base_name_id}{ext}" for ext in possible_extensions], + # 支持标题命名 + *[f"images/{base_name_title}{ext}" for ext in possible_extensions] + ] + + for path in all_possible_paths: + if os.path.exists(path): + image_path = path + break + + if image_path and os.path.exists(image_path): + print(f"图片:{image_path}") + else: + print("图片:无") + + def show_next_dialog(self): + """显示下一条对话""" + if not self.current_scene: + return + + conversation = self.current_scene["conversation"] + dialog_index = self.game_state.current_dialog_index + + if dialog_index >= len(conversation): + # 当前场景对话结束 + self._scene_complete() + return + + dialog = conversation[dialog_index] + + if dialog["type"] == "user_question": + # 区分选择题和自由回答题 + if "options" in dialog: + self._show_question(dialog) + else: + self._show_dynamic_question(dialog) + else: + # 显示普通对话 + self._add_message(dialog) + + # 自动显示下一个对话 + self.game_state.current_dialog_index += 1 + time.sleep(1) + self.show_next_dialog() + + def _add_message(self, dialog: Dict): + """添加对话消息""" + speaker = dialog["speaker"] + text = dialog["text"] + print(f"\n{speaker}: {text}") + + def _show_question(self, dialog: Dict): + """显示选择题""" + print(f"\n{dialog['question']}") + + for i, option in enumerate(dialog["options"]): + print(f"{i + 1}. {option}") + + # 获取用户输入 + try: + user_choice = int(input("请输入选项编号:")) - 1 + + if 0 <= user_choice < len(dialog["options"]): + self._check_answer(dialog, user_choice) + else: + print("无效的选项,请重新选择!") + self._show_question(dialog) + except ValueError: + print("请输入有效的数字!") + self._show_question(dialog) + + def _show_dynamic_question(self, dialog: Dict): + """显示动态问题(使用API调用)""" + print(f"\n{dialog['question']}") + user_input = input("请输入您的回答:") + + # 保存用户输入到对话历史 + self._save_user_input(dialog, user_input) + + # 获取AI响应 + ai_response = self._get_ai_response(user_input) + + # 显示AI响应 + print(f"\nAI响应: {ai_response}") + + # 继续对话 + self.game_state.current_dialog_index += 2 # 跳过用户问题和AI响应 + self.show_next_dialog() + + def _save_user_input(self, dialog: Dict, user_input: str): + """保存用户输入到对话历史""" + if not self.current_scene: + return + + conversation = self.current_scene["conversation"] + dialog_index = self.game_state.current_dialog_index + + # 插入用户输入 + user_message = { + "speaker": "我", + "text": user_input, + "type": "user" + } + + conversation.insert(dialog_index + 1, user_message) + + # 插入AI响应占位符 + ai_message = { + "speaker": "AI", + "text": "", + "type": "other" + } + + conversation.insert(dialog_index + 2, ai_message) + + def _get_ai_response(self, user_input: str) -> str: + """获取AI响应""" + if not self.api_key: + return "抱歉,API密钥未配置,无法获取AI响应。" + + try: + # 构建对话历史 + messages = [ + {"role": "system", "content": "你是一位英语学习助手,帮助用户进行日常对话练习。请用英语回复,保持对话自然流畅。"} + ] + + # 添加当前对话历史 + if self.current_scene: + for dialog in self.current_scene["conversation"][:self.game_state.current_dialog_index + 1]: + role = "assistant" if dialog["speaker"] != "我" else "user" + content = dialog.get("text", dialog.get("question", "")) + messages.append({"role": role, "content": content}) + + # 添加用户最新输入 + messages.append({"role": "user", "content": user_input}) + + # 调用API + headers = { + "Authorization": f"Bearer {self.api_key}", + "Content-Type": "application/json" + } + + payload = { + "model": "deepseek-chat", + "messages": messages, + "stream": True + } + + response = requests.post(self.api_url, headers=headers, json=payload, stream=True) + response.raise_for_status() + + # 处理流式响应 + ai_response = "" + print("\nAI正在思考...", end="", flush=True) + + for chunk in response.iter_content(chunk_size=None): + if chunk: + decoded_chunk = chunk.decode("utf-8") + # 解析SSE格式 + lines = decoded_chunk.split("\n") + for line in lines: + if line.startswith("data: "): + data = line[6:].strip() + if data != "[DONE]": + try: + json_data = json.loads(data) + if "choices" in json_data and json_data["choices"]: + delta = json_data["choices"][0].get("delta", {}) + if "content" in delta: + content = delta["content"] + ai_response += content + print(content, end="", flush=True) + except json.JSONDecodeError: + continue + + print() # 换行 + return ai_response if ai_response else "抱歉,无法生成响应。" + + except requests.exceptions.RequestException as e: + print(f"\nAPI调用错误: {e}") + return "抱歉,API调用失败。" + + def _check_answer(self, dialog: Dict, user_choice: int): + """检查答案""" + is_correct = user_choice == dialog["correctAnswer"] + + if is_correct: + self.game_state.correct_answers += 1 + print("\n✅ 回答正确!") + else: + correct_answer = dialog["options"][dialog["correctAnswer"]] + print(f"\n❌ 回答错误!正确答案是:{correct_answer}") + + self.game_state.total_questions += 1 + self.game_state.score += 1 if is_correct else 0 + + # 更新进度 + self.update_progress() + + # 继续对话 + self.game_state.current_dialog_index += 1 + time.sleep(1) + self.show_next_dialog() + + def _scene_complete(self): + """场景完成""" + print("\n" + "=" * 50) + print("当前场景对话已完成!") + print("=" * 50) + + if self.game_state.current_scene_index < len(english_data["scenes"]) - 1: + # 询问是否进入下一个场景 + next_scene = input("是否进入下一个场景?(y/n): ") + if next_scene.lower() == "y": + next_index = self.game_state.current_scene_index + 1 + self.load_scene(next_index) + else: + print("\n🎉 所有场景已完成!") + self._show_final_score() + + def _show_final_score(self): + """显示最终得分""" + if self.game_state.total_questions > 0: + accuracy = (self.game_state.correct_answers / self.game_state.total_questions) * 100 + print(f"\n最终得分: {self.game_state.score}") + print(f"正确率: {accuracy:.1f}%") + print(f"总题目数: {self.game_state.total_questions}") + print(f"正确答案: {self.game_state.correct_answers}") + else: + print("\n未完成任何题目") + + def update_progress(self): + """更新进度信息""" + total_scenes = len(english_data["scenes"]) + current_scene = self.game_state.current_scene_index + 1 + print(f"\n进度:场景 {current_scene}/{total_scenes} | 得分:{self.game_state.score}") + + def start(self): + """启动应用""" + print("欢迎使用英语学习程序!") + print("=" * 50) + + # 加载第一个场景 + self.load_scene(0) + +if __name__ == "__main__": + # 创建应用实例 + app = EnglishLearningApp() + + # 启动应用 + app.start()