技術細節
ICT Setter 的構造,供負責運行的 IT 人員,以及想了解由輸入題目到列印試卷之間發生甚麼事的人參考。
技術組合
| 部分 | 技術 |
|---|---|
| 介面 | React 19、TypeScript 及 Vite;Fluent UI 及 Base UI 組件,配上套件自家的藍綠色主題 |
| 伺服器 | Node.js、Express 及以 SQLite 為基礎的 Prisma,連線階段儲存在數據庫 |
| 文件 | 以 Python 填寫已審批的 Word 範本,再由 LibreOffice 轉換為 PDF |
| 圖表 | XeLaTeX 配合 TikZ,再以 dvisvgm 及 Poppler 輸出 SVG 及 PNG |
| 答案檢查器 | Python,並以 g++ 及 Free Pascal 處理 C++ 及 Pascal 答案 |
單一程式庫
packages/exam-core 試卷的定義:試卷模型、編解碼及驗證 packages/publication-contracts 列印及匯出請求的定義 packages/assessment-domain 考試規則:流程、鎖定、成績及分析 apps/assessment-server Assessment 的用例及 API apps/assessment-client Assessment 的介面 shared/ 編輯、協作及題庫 server/ 提供兩個應用程式的伺服器及背景程序 client/ ICT Setter 的介面 scripts/ 文件排版、TikZ 編譯器、答案檢查器 site/ 本網站
Assessment 從不引用擬卷部分的程式碼;邊界測試及 lint 規則確保這一點,因此考試部分只依賴試卷模型及自身的規則。
試卷模型
一份試卷是一個樹狀結構:分部、題目、分題及子題,以及其中的組件(文字、列表、表格、程式碼、圖片、圖表、答題空間、分頁)。每段文字都是中英文一對,採用一種精簡的富文字格式,可標示粗體、斜體、程式碼、底線、上標及加框空格。同一套編解碼及驗證在編輯器中(即時)及伺服器上(儲存前)運行。
每個節點都有穩定的識別碼,試卷會序列化為標準 JSON,作為雜湊的依據。修改以針對指定版本的操作傳送;同一時間只限一人編輯,編輯期間租用權會自動續期,而每個已儲存的版本都會保留。
由試卷到列印
- 試卷先轉為排版計劃:題號、分部標題、分頁及答題空間,一次計算,供中英文共用。
- Python 排版程式開啟已審批的 Word 範本,把試卷內容填入標記的區域,因此頁首、頁尾、封面及版面設定都直接來自範本。
- LibreOffice 把 Word 檔轉換為 PDF,用於預覽及列印。
範本以範本套件提供,經內容雜湊並須由管理員批准後才可使用。工作在背景程序執行,每項工作都會報告進度,讓介面顯示正在進行的步驟。
中文排版依循固定規則:內文用新細明體,粗體用華康儷粗黑,中文字加 1 點字距,英文字母及數字則不加。排版程式以試卷字型的實際字寬量度文字,用以把分數放在最後一行末端,並按內容調整表格及程式碼的闊度。
圖表
擬卷人只需撰寫繪圖指令,文件設定、字型及程式庫都已固定。可讀寫檔案、定義巨集或連接網絡的指令會在編譯前被拒絕。每個圖表的編譯都有時間及輸出上限,SVG 在儲存前只保留基本圖形。結果按原始碼、標籤及工具版本的雜湊值快取,未改動的圖表不會重複繪製。
考試
- 每個考試鎖定一個已鎖定的試卷版本及一個評卷參考版本,並記錄兩者及每張圖片的雜湊值。由派發試卷至報告,考試的每個步驟都經此鎖定讀取並加以核對。
- 考生收到的是試卷的白名單投影:題目、內容及以穩定識別碼標示的選項,絕不包括評卷參考、備註或出處。
- 由伺服器計時。已提交的答案由數據庫觸發器凍結,完成的批改亦不會再被修改:重新批改會新增一輪批改。
- 總分、等級、排名及統計都在讀取時由分數計算,從不儲存,因此不會與批改結果出現偏差。
- 每個計分分題都有等同性雜湊值,只有真正相同的分題才會跨考試合併統計。
答案檢查器
閱卷員的檢查程式以 Python 撰寫,不論考生用哪種語言作答。檢查程式設定輸入、執行答案、呼叫函數並列明預期結果;同一程式可用於 Python、Pascal 或 C++ 答案、SQL 語句或試算表公式。只寫了程式片段或把數據直接寫在程式中的答案,亦會按原樣處理。
考生的程式在獨立程序中執行,不繼承環境變數,只有自己的空白資料夾,並設有 5 秒時間上限、記憶體上限,以及檔案和輸出大小上限。
保安
- 職員帳戶只限邀請;連線階段儲存在伺服器。
- 會改動資料的請求都附有 CSRF 權杖。
-
考生密碼只顯示一次,並只以 scrypt 雜湊值儲存。考生的連線 cookie 設為
httpOnly及SameSite=Strict,只限考試 API 使用,登入亦有次數限制。 - 沒有考試角色的職員不會看到該考試;角色欠缺的權限會被拒絕。
測試
每個套件都有自己的 Vitest 測試;排版程式、TikZ 編譯器及答案檢查器以 pytest 測試;Playwright 則端對端操作兩個應用程式,包括考生應考的過程。持續整合會檢查格式、lint、型別及所有測試,在 Linux 上產生文件,並在 Windows 上測試 Windows 部署指令碼。