This repository holds the econ-database app. The mock papers, past papers, diagrams, question images, and the question bank are not in this public tree. They live only in the private data repository, under shared/. The hosted page loads shared/data/database.json through the Apps Script proxy after a known username signs in. GitHub Pages cannot read a private repository: a raw file URL for a private repo answers 404 unless a token is sent, and that token stays in Apps Script Script properties. The browser never sees the token, the owner, or the repository name.
Open the site over http or https. A file:// page cannot call the proxy. A local econ-database/data/database.json is only a developer fallback and is gitignored.
APP_VERSION in econ-database/js/constants.js is printed in the browser console as Question bank version: when the page loads. The same value is on the ?v= of every script in econ-database/index.html, so a new number also bypasses a cached copy of the scripts.
Bump both together on every change to the app or the question bank. Do not keep a record of what each version changed. A reader checks the console line against the number in constants.js to see whether the open page is current. GitHub Pages may keep the previous scripts for about ten minutes; the version line is how you tell.
econ-database/scripts/build_mock_questions.py is only the first import. Set ECON_DATA_ROOT to a checkout of the private data repository (the directory that contains shared/). The script reads every .docx in shared/papers/mock-tests/, splits Paper 1 and Paper 2 into questions, guesses a chapter (topic), specific concepts, and question patterns, and writes shared/data/database.json.
That parse is rough. Word files repeat text boxes, glue diagram labels into the stem, and sometimes split one question into two. Do not treat the script output as finished.
Each question object includes:
id: stable key, MT{paper}-P{1|2}-{nn}, for example MT35-P1-01. Paper numbers are 27–44 today.plainText, questionTextChi, or answerChi. The id already has it.topic: chapter name. This is the broad classification.concepts: specific ideas such as 機會成本 or 勞工生產力. Not the chapter name.patterns: how the question is asked (解釋, 計算, 圖表判讀, …). Do not put 多項選擇題 or 結構題 here. That distinction is questionType.plainText and questionTextChi: the same cleaned wording. plainText is the column a person reads.reviewedByAI: Y or N.lastReviewDate: YYYY-MM-DD when reviewedByAI is Y, otherwise an empty string.The builder loads the existing JSON before it writes. If reviewedByAI is Y, that whole record is copied back unchanged, even when the parser emits a new draft with the same id. Reviewed records whose id the parser no longer emits are still appended, so a boundary fix is not dropped.
After you correct a question by hand, set reviewedByAI to Y and lastReviewDate to the day you finished. Leave N on anything you did not read.
Running the script again is safe for reviewed rows. It will still refresh rows marked N.
Read the .docx and the JSON row together. For each question:
shared/papers/mock-tests/ on the private data checkout. Paper 1 files end in 卷一, Paper 2 in 卷二, marking schemes in 參考答案.[圖:供需曲線,原均衡 E0] instead of pasting the same axis labels three times. Do not assign a number to price or quantity unless the file states that pairing.plainText and questionTextChi.topic, concepts, and patterns against that cleaned wording. Fix them if the draft was based on junk text.reviewedByAI to Y and lastReviewDate to today only after that read.Read econ-database/data/vocabulary.json before you classify a new paper. It lists the concepts, question patterns, diagram types, and table types already used.
concepts are specific ideas such as 機會成本 and 勞工生產力. Do not put the chapter name there.patterns describe how the question is asked. Do not repeat questionType.graphType is a diagram type from diagramTypes, or - when there is no diagram. Do not leave it as 圖.tableType is a table type from tableTypes, or - when there is no table. Do not leave it as 表格.calculationType is a calculation type from calculationTypes, or -. Do not leave it as 計算. Wording such as 包括在本地生產總值的計算之內 is not a calculation question.multipleSelectionType is a combination type from multipleSelectionTypes, or -. Do not leave it as 複選. Three numbered statements are 三項陳述組合; four are 四項陳述組合. A 哪幅圖 item whose choices are numbered diagrams is 圖選組合.vocabulary.json by hand. Every time the builder writes database.json, it rewrites vocabulary.json from the concepts, patterns, diagram types, and table types on those questions, and it keeps labels already in the file. After you change labels without a full import, run python3 econ-database/scripts/build_mock_questions.py --sync-vocabulary. That reads database.json and updates vocabulary.json only.shared/data/database.json also holds questions published by HKEAA (HKDSE, HKCEE, and HKALE). They use the same fields as the mock papers. publisher is HKEAA or 雅集出版社. The filter row has a 出版商 control that includes or excludes either publisher. A publisher badge on a question card applies the same filter.
Import another HKEAA export with:
python3 econ-database/scripts/build_mock_questions.py --import-hkeaa path/to/export.json
The importer classifies topic, concepts, patterns, diagram type, table type, calculation type, and combination type from the wording. New rows stay reviewedByAI N. A row already marked Y is not replaced. It refreshes vocabulary.json in the same run.
Question images for the mock papers are cut from the official PDF, not from the Word file. The Chinese wording still comes from the Word file. questionTextEng is the English paper when the question number and, for multiple choice, the answer letter match the Chinese record. The matching English question image is originalQuestionImageEng.
Multiple-choice answer images are the「答案解釋」block in the Chinese solution PDF, and the matching「Explanation」block in the English solution PDF (originalAnswerImageEng). Each English multiple-choice answer is kept only when its letter matches the Chinese key. Written questions (SQ/LQ, Paper 2) also have an English answer image, cropped from the Paper 2 section of the English solution. The blank ruled lines left for students to write on (答案線) are not part of the question and are removed from both the Chinese and English question images. Paper 2 crops also drop the running header and footer, including the box around those lines: 「Please stick the barcode label here.」, 「請在此貼上電腦條碼」, 「Answers written in the margins will not be marked.」, and 「寫於邊界以外的答案,將不予評閱。」. python3 econ-database/scripts/capture_from_pdf.py --sq-images repeats that Paper 2 update.
HKDSE papers for 2012–2025 live in shared/papers/past-papers/ as one Chinese file and one English file per year. Each file contains the question papers, the marking scheme, and the markers’ comments. python3 econ-database/scripts/capture_hkeaa.py cuts question images, written-answer images, and the exam-report image for a question. A crop is kept only when its wording agrees with the Chinese plain text or the English question text already stored for that question. Multiple-choice answer keys are not cropped. Question images also drop the year-and-page footer, such as 2025-DSE-ECON 1-14, including when that line sits in the middle of a crop that crosses a page. python3 econ-database/scripts/capture_hkeaa.py --strip-footers repeats that cleanup. Report images use the 報告 and 英報告 buttons.
Crops that do not match the stored wording are left out. econ-database/HKEAA-待補圖片.md lists every year from 2012 to 2025 that still needs a picture, and the filenames to use. 2013 was not cropped at all. The other years include only the crops that matched.
econ-database/DSE-題目規律.md collects the past-paper patterns (answer-key balance, diagrams, calculations, and wording). It keeps the notes already compiled by hand and fills only the years whose stems are in the question bank.
One button, AI出題, asks for new questions from the current filter or from questions the user pastes. It stays hidden unless the proxy returns ai: true. The modal picks a 出題模式, an editable 出題指示, and one of three models, and can send a short 測試 ping that does not use the reference questions. The browser calls a Google Apps Script web app. The upstream API key and the role hashes stay in that script’s Script properties, not in this repository. Production roles are SHA-256 hex lists only (ALLOWED_ADMIN_HASHES, ALLOWED_AI_HASHES, ALLOWED_RESTRICTED_HASHES). Successful replies are also backed up to a GenerationBackup sheet tab. Admin steps, including the required new deployment version after Code.gs changes: econ-database/apps-script/README.md. The question bank the page loads is shared/data/database.json in the private data repository, read by fetchSharedAsset. It is not a file in this public repository.
自動同步, 上傳到 GitHub, and 從 GitHub 載入 appear only when githubSync is true (the admin role). AI editors do not see them. Those buttons use the same web app. The GitHub token, owner, and private repository name stay in Script properties. Do not commit them. The script stores that user’s question file and model replies under users/<username>/ in the private repository. The page does not choose the path.
shared/papers/mock-tests/ on the private data checkout: 卷一, 卷二, and 參考答案. Keep the same filename style as the papers already there. ECON_DATA_ROOT must point at that checkout.PAPER_NUM in the builder if it is not already listed.python3 econ-database/scripts/build_mock_questions.py. New ids arrive with reviewedByAI N. Existing Y rows stay as they are.N row using the steps above. Do not mark Y from the script alone.shared/data/database.json, shared/data/vocabulary.json, and the new docx files in the private data repository. The builder refreshes vocabulary.json in the same run. Do not commit those files into this public repository.Per-user Git sync stays at users/<username>/. Shared content is a different tree. GITHUB_SHARED_PREFIX defaults to shared.
shared/data/database.json — question bank the site loadsshared/data/database.js — same bank as a script, for tools that still write itshared/data/vocabulary.json — labels collected from the bankshared/diagrams/ — inline diagram images (diagrams/... on each question)shared/originals/ — question, answer, and report crops (originals/... on each question)shared/papers/mock-tests/ — mock-paper Word and PDF packsshared/papers/past-papers/ — HKDSE PDF packsshared/build/ — classification JSON and JSONL used by the import scriptsStage a copy from this repository’s history into a local private checkout, then commit there:
python3 econ-database/scripts/stage_shared_assets.py --check
ECON_DATA_ROOT=/path/to/private/checkout python3 econ-database/scripts/stage_shared_assets.py
The script does not take a token and does not name the private repository. --check only validates paths. The copy reads the working tree when the assets are still present, otherwise the newest ancestor commit that still contains econ-database/data/database.json. Use a full clone for that fallback.
Import scripts read and write those paths when ECON_DATA_ROOT is set. See econ-database/scripts/data_paths.py.
The public Pages site is mas-repo/econ-database2. Publish from this repository’s econ-database/ folder after this change. Do not copy MockTests/, PastPaper/, econ-database/diagrams/, econ-database/originals/, or econ-database/data/*.json from an older commit. Those paths are gitignored here so a normal copy of the folder does not include them. The old mas-repo/econ-database repository is not part of this change.