Files
PastpaperMaster/backend/BATCH_IMPORT_GUIDE.md
2026-04-24 23:19:34 +09:00

275 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 批量导入试卷 — 交接文档
## 概述
`batch_import.py` 用于批量向 PastPaper Master 数据库填充试卷。它会自动完成:
1. 创建 DB 记录
2. 上传 PDF 到 Supabase Storage
3. Gemini Vision 提取题目结构
4. DeepSeek 生成 AI 解题三件套knowledge reminder + hint + solution
---
## 凭据 & 账号
### .env 文件(项目根目录)
```env
## Supabase
SUPABASE_URL=https://pvcxipwovpwrurebouwg.supabase.co
SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InB2Y3hpcHdvdnB3cnVyZWJvdXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzM0MDAzMzIsImV4cCI6MjA4ODk3NjMzMn0.pq9JhSSdok4eHOul7rmLLN7AjXNCw0Mz8fxXEu-eQLY
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InB2Y3hpcHdvdnB3cnVyZWJvdXdnIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc3MzQwMDMzMiwiZXhwIjoyMDg4OTc2MzMyfQ.JUlHLKYhf7MaLU_YfmqUXmCBgQOv3vEbsSUke6tS41w
SUPABASE_DB_PASSWORD=nyddiq-5mefde-senSih
## LLM
DEEPSEEK_BASE_URL=https://api.deepseek.com/v1
DEEPSEEK_API_KEY=sk-f7768364050d4a38bb0f42030ea138da
GOOGLE_GEMINI_API_KEY=AIzaSyBm_SMw5iwxn5KxWmVoyAJMMpjfu86m-yU
# 以下备用,批量导入不需要
LAOZHANG_BASE_URL=https://api.laozhang.ai/v1
LAOZHANG_API_KEY=sk-oqKIhugRggjtjzPg0e07Bb2aB9Fb44B2A904BfA1E9C67947
DASHSCOPE_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
DASHSCOPE_API_KEY=sk-20164094f29e416aad0437d9e678b04f
```
### 管理后台
| 服务 | 地址 | 账号 | 用途 |
|------|------|------|------|
| **Supabase** | https://supabase.com/dashboard | 找 soda 要登录 | 数据库管理、Storage、Auth |
| **Google AI Studio** | https://aistudio.google.com | 用自己的 Google 账号API key 在 .env 里 | 监控 Gemini 用量,免费额度 |
| **DeepSeek** | https://platform.deepseek.com | 用 .env 里的 key 登录 | 监控用量和余额,**这个会花钱** |
| **Gitea** | https://git.deepknow.site | 用户名 `soda`,密码 `Jermaine0805` | 代码仓库 |
### API 费用监控(重要!)
| 模型 | 用途 | 单价 | 单份试卷约费用 |
|------|------|------|------|
| Gemini 2.5 Flash | Vision 提取 + 答案匹配 | 免费额度(每分钟有限) | $0 |
| DeepSeek V3 | AI trio 生成 | $0.28/M input, $1.10/M output | ~$0.5-1.5 |
**批量导入前先去 DeepSeek 平台看余额!** 50 份试卷大约消耗 $25-75。
---
## 服务器信息
| 项目 | 值 |
|------|-----|
| 生产服务器 | `129.226.210.66` |
| SSH | `ssh -i ~/.ssh/id_ed25519 root@129.226.210.66` |
| 后端容器 | `pastpaper-backend-1` |
| 项目路径 | `/opt/pastpaper/` |
| 前端静态文件 | `/opt/1panel/www/pastpaper/` |
| Gitea 服务器 | `43.134.230.28`1Panel 面板管理) |
---
## 第一步:获取试卷 PDFPeterGao Scraper
### 概述
HKUST 历年试卷来源: https://petergao.cc/ustpastpaper/
该网站有 quota 限制(每账号每学期 50 次下载),需要多个 HKUST 学生账号协作下载。
### Scraper 位置
```bash
cd /path/to/PastPaper\ Master/pastpaper-scraper/
```
GitHub repo: https://github.com/ZhaoYiping789/PeterGao-raper.git
### 环境
```bash
# 安装 uv
curl -LsSf https://astral.sh/uv/install.sh | sh
# 安装依赖
uv sync
```
### 运行
```bash
# 批量下载(按 group 分配)
uv run python batch_download.py --group B
```
流程:
1. 脚本显示本轮要下载的课程(每轮不超过 45 quota
2. 输入一个 ITSC 用户名(如 `chanxm`
3. 网站向该用户发验证邮件
4. **让该用户把邮件链接发给你**
5. 粘贴链接到脚本
6. 自动下载,完成后换下一个账号
### 获取账号
需要找 HKUST 学生要 ITSC 账号配合。每个账号每学期可用 50 quota全站约 3400 份试卷需要约 52 个账号。联系 soda 协调。
### 下载结果
PDF 保存在 `pastpaper-scraper/papers/{课程名}/` 目录下。
---
## 第二步:整理文件结构
将 scraper 下载的 PDF 整理成以下结构:
```
papers_to_import/
├── COMP2211/
│ ├── 2024_spring_midterm.pdf
│ ├── 2024_spring_midterm_answer.pdf <- 答案,自动匹配
│ ├── 2024_fall_final.pdf
│ └── 2023_spring_midterm.pdf
├── COMP2011/
│ └── ...
├── MATH1014/
│ └── ...
└── FINA2303/
└── ...
```
规则:
- 一级目录名 = 课程代码(自动转大写)
- 文件名: `{year}_{term}_{examtype}.pdf`
- 答案: `{year}_{term}_{examtype}_answer.pdf`(可选)
- term: `spring` / `fall` / `summer`
- examtype: `midterm` / `final` / `quiz`
scraper 下载的文件名格式比较杂(如 `(COMP2211)[2024](s)midterm~xxx.pdf`),需要手动或写脚本重命名。
### 优先导入的课程
用户量大,优先补充:
- COMP2011, COMP2211, COMP2711H
- MATH1013, MATH1014, MATH2023
- PHYS1112
- ELEC2100
- FINA2303
---
## 第三步:批量导入
### 在本地运行(推荐)
```bash
cd /path/to/PastPaper\ Master/backend
source .venv/bin/activate # 或 .venv/bin/python
# 试运行(不实际导入,只打印)
python batch_import.py /path/to/papers_to_import/ --batch --dry-run
# 正式导入(串行)
python batch_import.py /path/to/papers_to_import/ --batch
# 并发导入(最多 2 个同时,别超过 2
python batch_import.py /path/to/papers_to_import/ --batch --concurrency 2
```
### 单份导入
```bash
python batch_import.py paper.pdf \
--course COMP2211 --year 2024 --term spring --exam midterm
# 带答案
python batch_import.py paper.pdf --answer answer.pdf \
--course COMP2211 --year 2024 --term spring --exam midterm
```
### 自动查重
脚本会跳过已存在的试卷(相同 course_code + year + term + exam_type
---
## 处理时间估计
| 阶段 | 耗时 |
|------|------|
| PDF 渲染 | 2-5s |
| Vision 提取(每 8 页一批) | 30-60s/批 |
| 答案匹配 | 20-40s |
| AI trio 生成(每 3 题一批) | 15-25s/批 |
| **总计30 题试卷)** | **~3-5 min** |
| **总计40+ 题试卷)** | **~5-8 min** |
并发不要超过 2Gemini 会限流429 错误,脚本自动重试但更慢)。
---
## 常见问题
### Q: 处理失败status=error怎么办
```bash
cd backend/
.venv/bin/python -c "
import sys; sys.path.insert(0, '.')
from dotenv import load_dotenv; load_dotenv('../.env')
from app.services.supabase_client import get_supabase
sb = get_supabase()
errors = sb.table('papers').select('id, course_code').eq('status', 'error').execute().data
for p in errors:
sb.table('paper_questions').delete().eq('paper_id', p['id']).execute()
sb.table('papers').delete().eq('id', p['id']).execute()
print('Deleted', p['course_code'])
"
```
然后重新导入即可。
### Q: 只重新生成 AI trio题目已提取成功
```bash
.venv/bin/python -c "
import sys; sys.path.insert(0, '.')
from dotenv import load_dotenv; load_dotenv('../.env')
from app.services.supabase_client import get_supabase
sb = get_supabase()
PAPER_ID = 'xxxxxxxx-xxxx-...' # 替换为实际 ID
qs = sb.table('paper_questions').select('id').eq('paper_id', PAPER_ID).execute().data
for q in qs:
sb.table('paper_questions').update({'solution': None, 'ai_hint': None, 'knowledge_reminder': None}).eq('id', q['id']).execute()
sb.table('papers').update({'status': 'processing'}).eq('id', PAPER_ID).execute()
print(f'Reset {len(qs)} questions')
"
# 重启后端触发自动续传
ssh -i ~/.ssh/id_ed25519 root@129.226.210.66 "sudo docker restart pastpaper-backend-1"
```
### Q: 如何部署后端代码改动?
```bash
scp -i ~/.ssh/id_ed25519 app/services/paper_processor.py root@129.226.210.66:/opt/pastpaper/backend/app/services/
ssh -i ~/.ssh/id_ed25519 root@129.226.210.66 "cd /opt/pastpaper && sudo docker compose up -d --build backend"
```
### Q: 如何部署前端改动?
```bash
cd frontend && npm run build
cp public/favicon.jpg dist/
ssh -i ~/.ssh/id_ed25519 root@129.226.210.66 "rm -rf /opt/1panel/www/pastpaper/assets"
scp -i ~/.ssh/id_ed25519 dist/index.html dist/favicon.jpg root@129.226.210.66:/opt/1panel/www/pastpaper/
scp -i ~/.ssh/id_ed25519 -r dist/assets root@129.226.210.66:/opt/1panel/www/pastpaper/
```
### Q: DeepSeek 余额不够了?
去 https://platform.deepseek.com 充值。用的是 DeepSeek V3deepseek-chat很便宜但批量跑几百份也会花几十美元。
### Q: Gemini 限流429
免费额度有每分钟请求限制。脚本内置自动重试(指数退避),等几秒就好。如果频繁 429降低并发到 1。