Spaces:
Paused
Paused
| title: Advanced SCU Course Catcher | |
| emoji: 🐳 | |
| colorFrom: green | |
| colorTo: yellow | |
| sdk: docker | |
| app_port: 7860 | |
| pinned: false | |
| # Advanced SCU Course Catcher | |
| 这是一个面向 Hugging Face Space 的四川大学选课系统 Web 版抢课面板。当前版本已经从单机脚本改造成了可部署、可并行、可多用户管理的 Flask + Selenium 服务,支持普通用户端和管理员端分离运行。 | |
| ## 当前能力 | |
| - 普通用户入口:`/login` | |
| 用户使用 `学号 + 密码` 登录,只能看到自己的课程、任务状态和实时日志。 | |
| - 管理员入口:`/admin` | |
| 管理员使用 `账号 + 密码` 登录。此入口不会在用户登录页展示。 | |
| - 多管理员体系 | |
| 支持多位普通管理员,支持 1 位超级管理员。超级管理员账号和密码由环境变量 `ADMIN`、`PASSWORD` 提供。 | |
| - 多用户管理 | |
| 管理员可以手动录入用户账号、重置密码、启用/禁用用户、查看所有课程目标。 | |
| - 课程录入 | |
| 用户自己填写 `课程号` 和 `课序号`,管理员可见全部内容,也可以代为录入和修改。 | |
| - 实时日志 | |
| 用户和管理员都可以实时看到程序日志,前端通过 SSE 持续推送。 | |
| - 并行调度 | |
| 多用户任务由后台任务调度器并行执行,管理员可以在后台动态设置并行数。 | |
| - Hugging Face Space 适配 | |
| 使用 Docker Space 运行,内置 Chromium、chromedriver、中文字体和无头浏览器配置。 | |
| - 登录验证码与提交验证码 OCR | |
| 登录阶段和提交选课阶段都支持验证码 OCR 自动识别。 | |
| - Selenium 自恢复 | |
| 单个 Selenium 会话连续错误达到 5 次时,会自动重建浏览器;连续重建 5 个会话仍失败时,任务才会终止。 | |
| ## 运行结构 | |
| 当前实际运行入口和核心模块如下: | |
| - `app.py` | |
| Hugging Face / gunicorn 使用的 WSGI 入口。 | |
| - `space_app.py` | |
| Flask 路由、页面渲染、登录鉴权、SSE 日志流。 | |
| - `core/config.py` | |
| 运行配置与内部密钥管理。 | |
| - `core/db.py` | |
| SQLite 数据层。 | |
| - `core/task_manager.py` | |
| 并行调度、任务生命周期管理。 | |
| - `core/course_bot.py` | |
| Selenium 抢课核心逻辑、验证码识别、重试与重建策略。 | |
| - `webdriver_utils.py` | |
| Chromium / chromedriver 初始化。 | |
| - `templates/` + `static/` | |
| 用户端、管理员端和响应式前端页面。 | |
| ## 环境变量 | |
| 为了减少 Space 配置复杂度,当前只保留少量必要环境变量。 | |
| ### 必填 | |
| - `ADMIN` | |
| 超级管理员账号。 | |
| - `PASSWORD` | |
| 超级管理员密码。 | |
| ### 可选 | |
| - `DATA_DIR` | |
| 数据目录,默认会使用仓库下的 `data/`。在 Hugging Face Space 中建议保持为 `/data`。 | |
| - `CHROME_BIN` | |
| 自定义 Chromium 路径。默认会自动尝试 `/usr/bin/chromium`。 | |
| - `CHROMEDRIVER_PATH` | |
| 自定义 chromedriver 路径。默认会自动尝试 `/usr/bin/chromedriver`。 | |
| ### 不需要再手动配置的内容 | |
| 以下内容已经改为自动生成或写入程序默认值,不需要再手工配置环境变量: | |
| - Flask session secret | |
| - 用户密码加密密钥 | |
| - 默认并行数 | |
| - 登录重试次数 | |
| - Selenium 会话错误阈值 | |
| - Selenium 会话重建阈值 | |
| - 提交验证码重试次数 | |
| - 页面超时时间 | |
| - 日志页容量 | |
| 程序会在 `DATA_DIR/.app_secrets.json` 中自动持久化内部密钥。这样即使以后修改 `ADMIN` 或 `PASSWORD`,也不会导致历史用户密码全部失效。 | |
| ## Hugging Face Space 部署 | |
| ### 1. 创建 Space | |
| 在 Hugging Face 上创建一个新的 Space: | |
| - SDK 选择 `Docker` | |
| - 端口使用 `7860` | |
| - 仓库内容直接推送本项目即可 | |
| 本仓库根目录已经提供好 `README.md` 的 Space metadata 和 `Dockerfile`。 | |
| ### 2. 设置 Space Secrets | |
| 进入 `Settings -> Variables and secrets`,至少配置: | |
| - `ADMIN=你的超级管理员账号` | |
| - `PASSWORD=你的超级管理员密码` | |
| 通常不需要再设置其他环境变量。 | |
| 如果你想显式指定数据目录,也可以增加: | |
| - `DATA_DIR=/data` | |
| ### 3. 开启持久化存储 | |
| 如果你希望以下数据在 Space 重启后仍然保留,建议在 Space 设置里开启 Persistent Storage: | |
| - 用户账号 | |
| - 管理员账号 | |
| - 课程目标 | |
| - 任务历史 | |
| - 运行日志 | |
| - 自动生成的内部密钥文件 | |
| 当前 Dockerfile 已按 `/data` 目录进行适配,并预先处理了目录权限,方便在 Space 中直接持久化。 | |
| ### 4. 推送部署 | |
| 如果你使用 git 推送到 Hugging Face Space,可以参考: | |
| ```bash | |
| git remote add space https://huggingface.co/spaces/<your-name>/<your-space> | |
| git push space main | |
| ``` | |
| ### 5. 启动方式 | |
| 容器启动命令已经写入 `Dockerfile`: | |
| ```bash | |
| gunicorn --bind 0.0.0.0:${PORT:-7860} --workers 1 --threads 8 --timeout 180 app:app | |
| ``` | |
| 这里使用 `1` 个 gunicorn worker,是为了避免多进程情况下每个进程都各自启动一套本地任务调度器。并发能力由应用内部的线程调度和管理员可配置的任务并行数控制。 | |
| ## Space 端建议配置 | |
| 推荐的初始运行策略: | |
| - 后台并行数先设置为 `1` 或 `2` | |
| - 只有在 Space CPU / 内存足够稳定时,再逐步提升 | |
| - 在真实高峰前,先做一次小规模联调 | |
| 对于 CPU 较小的 Space,同时拉起过多 Chromium 会明显增加失败率,因此管理员后台提供了并行数动态调整功能。 | |
| ## 管理员联调清单 | |
| 部署到 Space 后,建议按以下顺序联调: | |
| 1. 访问 `/admin`,使用 `ADMIN` / `PASSWORD` 登录超级管理员。 | |
| 2. 在管理员后台创建 1 个普通管理员、2 个测试用户。 | |
| 3. 为两个测试用户分别录入不同课程号和课序号。 | |
| 4. 将并行数设置为 `2`。 | |
| 5. 分别触发两个用户任务,确认两条任务都能进入队列,并按并行数启动。 | |
| 6. 在管理员日志面板确认日志持续刷新,且可以看到不同学号的执行记录。 | |
| 7. 使用用户账号访问 `/login`,确认用户只能看到自己的课程和日志。 | |
| 8. 在任务运行过程中测试停止任务,确认状态能变为“停止中”并最终收敛。 | |
| 9. 如果学校页面出现提交验证码,确认日志中能看到提交阶段验证码识别提示。 | |
| ## 自动化测试 | |
| 仓库当前已补充以下自动化测试: | |
| - `tests/test_config.py` | |
| 验证内部密钥在超级管理员账号变更后依然保持稳定。 | |
| - `tests/test_course_bot.py` | |
| 验证 Selenium 会话错误累计后会触发浏览器重建,并验证提交阶段验证码分支。 | |
| - `tests/test_task_manager.py` | |
| 验证任务调度器会严格遵守管理员设置的并行上限。 | |
| - `.github/workflows/linux-tests.yml` | |
| 在 `ubuntu-latest` 上自动运行上述测试,避免关键逻辑只在 Windows 本地验证。 | |
| 本地运行测试: | |
| ```bash | |
| python -m unittest discover -s tests -v | |
| ``` | |
| ## 本地运行 | |
| ### 直接运行 | |
| ```bash | |
| pip install -r requirements.txt | |
| set ADMIN=admin | |
| set PASSWORD=change-me | |
| python main.py | |
| ``` | |
| ### Docker 运行 | |
| ```bash | |
| docker build -t advanced-scu-course-catcher . | |
| docker run --rm -p 7860:7860 \ | |
| -e ADMIN=admin \ | |
| -e PASSWORD=change-me \ | |
| advanced-scu-course-catcher | |
| ``` | |
| ## 重要说明 | |
| - 本项目当前的任务模型是“持续轮询直到成功、手动停止或达到错误上限”。 | |
| - 用户密码会以应用内部密钥加密后保存在数据库中,用于后台自动登录教务系统。 | |
| - 管理员可以看到全部用户、课程和日志,请在实际使用前明确权限边界。 | |
| - 如果学校选课页面 DOM 结构变化较大,需要同步更新 `core/course_bot.py` 中的选择器以及 `javascript/` 下的辅助脚本。 | |
| - `course_catcher/` 目录是仓库中保留的旧实现,当前运行链路以 `space_app.py + core/*` 为准。 | |