SACC / README.md
cacode's picture
Update Space: schedule, MySQL persistence, registration codes, registration flow
a30f196 verified
metadata
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 位超级管理员。超级管理员账号和密码由环境变量 ADMINPASSWORD 提供。
  • 多用户管理 管理员可以手动录入用户账号、重置密码、启用/禁用用户、查看所有课程目标。
  • 课程录入 用户自己填写 课程号课序号,管理员可见全部内容,也可以代为录入和修改。
  • 实时日志 用户和管理员都可以实时看到程序日志,前端通过 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 中自动持久化内部密钥。这样即使以后修改 ADMINPASSWORD,也不会导致历史用户密码全部失效。

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,可以参考:

git remote add space https://huggingface.co/spaces/<your-name>/<your-space>
git push space main

5. 启动方式

容器启动命令已经写入 Dockerfile

gunicorn --bind 0.0.0.0:${PORT:-7860} --workers 1 --threads 8 --timeout 180 app:app

这里使用 1 个 gunicorn worker,是为了避免多进程情况下每个进程都各自启动一套本地任务调度器。并发能力由应用内部的线程调度和管理员可配置的任务并行数控制。

Space 端建议配置

推荐的初始运行策略:

  • 后台并行数先设置为 12
  • 只有在 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.ymlubuntu-latest 上自动运行上述测试,避免关键逻辑只在 Windows 本地验证。

本地运行测试:

python -m unittest discover -s tests -v

本地运行

直接运行

pip install -r requirements.txt
set ADMIN=admin
set PASSWORD=change-me
python main.py

Docker 运行

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/* 为准。