Add Python version of the English learning app with API integration

This commit is contained in:
Student 2026-01-09 10:15:53 +08:00
parent 5db1a62e0f
commit c53c77f2a2
4 changed files with 655 additions and 1 deletions

232
data.py Normal file
View File

@ -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"
}
]
}
]
}

66
html_python_guide.md Normal file
View File

@ -0,0 +1,66 @@
# HTML与Python集成指南
## 技术限制说明
HTML文件**不能直接引用或执行Python文件**,这是因为:
1. **运行环境不同**
- HTML/JavaScript 运行在浏览器端(客户端)
- Python 运行在服务器端或本地命令行环境
2. **执行方式不同**
- JavaScript 是解释型语言浏览器内置了JavaScript引擎
- Python 需要安装Python解释器才能执行
3. **语法和API完全不同**
- HTML只能识别并加载JavaScript文件通过`<script>`
- Python文件.py无法被浏览器直接解析和执行
## 可行的解决方案
### 方案1使用PyScript推荐
PyScript是一个允许在浏览器中直接运行Python代码的框架。它提供了以下功能
```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Python in HTML</title>
<!-- 引入PyScript -->
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css">
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
</head>
<body>
<!-- 直接在HTML中编写Python代码 -->
<py-script>
print("Hello from Python in HTML!")
# 可以导入Python模块和使用Python语法
</py-script>
</body>
</html>
```
### 方案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框架
请根据您的需求选择合适的方案。

29
main.py
View File

@ -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__":

329
script.py Normal file
View File

@ -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()