本文件提供給維護者與開發者,說明專案架構、資料模型、關鍵流程與擴充建議。
- Web Framework: FastAPI
- Template Engine: Jinja2
- ORM: SQLAlchemy 2.x
- Database: SQLite(每週一個 DB)
- Frontend: Server-rendered HTML + vanilla JS
依賴請見 requirements.txt。
app.py # 應用入口,掛載路由與中介層
models.py # SQLAlchemy 模型 + 多週 DB/session 管理
utils.py # 檔案工具函式(副檔名、hash、size)
core/security.py # CSRF token 注入/驗證
routers/
admin.py # 管理端重置
dashboard.py # 儀表板(單週 / 跨週)
imports.py # 匯入入口
grading.py # 評分入口
results.py # 成績與覆寫、匯出
files.py # 檔案下載與預覽
services/
import_service.py # 匯入核心邏輯
grading_service.py # 評分資料查詢 + upsert
results_service.py # 成績整併、覆寫、CSV 匯出
dashboard_service.py # 儀表板統計
preview_service.py # DOCX/MD/PPTX 預覽 HTML 生成
staff_service.py # 預設 TA/Professor 初始化
templates/ # Jinja2 模板
uploads/ # 上傳檔案(uploads/weekNN)
week_dbs/ # SQLite(weekNN.db)
app.py:
- 建立
FastAPI與Jinja2Templates init_security(app, templates):啟用 CSRF middleware 與模板 helper- 初始化 app state:
upload_folder = "uploads"page_size = 50default_rubric(由DEFAULT_RUBRIC解析)
- 掛載 static/uploads
- include routers
- startup 事件執行
init_db()(預設 week01)
- 每週獨立 SQLite:
week_dbs/weekNN.db - 週次由 query 參數
?week=weekNN決定 normalize_week()會將1,week1,Week 01正規化為week01
- 路由透過
Depends(get_db)取得 session get_db會:
- normalize week
- ensure schema + staff 初始化(lazy)
- yield session
- finally close session
dashboard_service.get_all_weeks_stats() 會遍歷 list_existing_weeks(),逐一開啟各週 session 彙整資料。
models.py:
Student(student_id, name)Submission(student_id FK, filename, file_hash unique, file_path, file_type...)TA(name unique)Professor(name unique)Grade(submission_id FK, ta_id FK, score, cr1..cr4, comments)GradeOverride(submission_id unique FK, professor_id FK, final_score, reason)
重要約束:
Grade: (submission_id,ta_id) uniqueGrade/GradeOverride分數有 0~100 check constraintGradeOverride: 每份 submission 最多一筆覆寫
GET /:單週儀表板GET /dashboard/all-weeks:跨週儀表板GET/POST /import:匯入頁與上傳處理GET/POST /grade:評分頁與提交GET /results:成績頁POST /override:教授覆寫GET /export:匯出 CSVGET /download/{submission_id}:檔案下載/inlineGET /preview/{submission_id}:預覽頁POST /admin/reset:重置當週資料
services/import_service.py:
- 將 multipart 資料先寫入 temp dir
- 過濾非法路徑與不支援檔案
- 掃描候選檔案,計算 SHA256
- 以資料夾名/檔名推斷學號姓名
- 批次查既有 hash 與 student
- 複製檔案到
uploads/weekNN/ - 建立
Submission記錄
注意:
- 重複檔案判定依
file_hash - 同名檔案會自動加
_1,_2避免覆蓋
grading_service.upsert_grade:
- 若該 TA 已評該 submission -> update
- 否則若該 submission 已有 >=2 筆 -> 拋錯拒絕
- 否則新增新評分
results_service.get_results_page_data:
avg_score= 既有 TA score 平均final_score=override.final_score(若存在)否則avg_score
core/security.py:
- 模板
get_csrf_token(request)注入 hidden input - middleware 確保 cookie
csrf_token存在 - POST 行為呼叫
verify_csrf(request, submitted_token)
重點:
- 表單需包含
csrf_token - JS fetch(例如 reset)需帶
X-CSRF-Token
services/preview_service.py:
- DOCX:
docx-preview前端渲染 - Markdown:
marked + DOMPurify - PPTX: 目前回傳「下載原檔」提示頁
這段是純 HTML 字串組裝,若未來改成 API + SPA,可拆分為靜態資源與前端框架。
安裝:
pip install -r requirements.txt啟動:
python app.py快速語法檢查:
python -m compileall app.py routers services core models.py utils.py目前專案未包含完整自動化測試,建議補上:
- 匯入流程(副檔名過濾、hash 去重、學號姓名解析)
- 週次隔離(同 submission 跨週不互相污染)
- TA 評分上限(每 submission 最多 2 位)
- 覆寫優先權(final score 覆蓋平均分)
- CSRF 驗證(缺 token/不一致 token)
建議使用 pytest + httpx/TestClient。
- 增加身份驗證與角色權限(TA/Professor/Admin)
- 將 staff 初始化改成可配置資料來源(DB migration 或 seed script)
- 增加 migration 工具(Alembic)
- 支援 S3/物件儲存,避免本地檔案依賴
- 為
dashboard_service增加聚合查詢優化
- SQLite 適合單機/中小規模,不適合高併發
secure=Falsecookie 設定僅適用本機開發,正式環境需 HTTPS + secure cookiepreview_service使用外部 CDN(需網路)