Skip to content

Feat/v2 display drill output - #1

Closed
TDXu wants to merge 14 commits into
mainfrom
feat/v2-display-drill-output
Closed

Feat/v2 display drill output#1
TDXu wants to merge 14 commits into
mainfrom
feat/v2-display-drill-output

Conversation

@TDXu

@TDXu TDXu commented Jul 21, 2026

Copy link
Copy Markdown
Owner

No description provided.

TD_Xu and others added 14 commits July 20, 2026 17:47
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
- scripts/studylib/render_html.py: parse_options + render_quiz_html
- templates/quiz.html.j2: inline CSS+JS, reveal toggle, per-question grading
- tests/test_render_html.py: 6 tests (half/full-width, none, continuation, embed, reveal off)

parse_options accepts both half-width (A-H/a-h) and full-width (A-Ha-h)
option letters, normalizing to ASCII uppercase via NFKC so downstream
rendering and tuples always get A/B/….

Co-Authored-By: Claude <noreply@anthropic.com>
In _question_for_render, option letters were normalized to ASCII uppercase
(via unicodedata.normalize('NFKC', ...).upper()), but the answer letters used
for grading (data-answer) and the displayed answer were not. A question whose
answer was lowercase (e.g. 'abc') or full-width would silently grade wrong:
the JS compares uppercase option letters against un-normalized answer letters.

Normalize answer letters the same way, and join them so the displayed
'答案:<b>{{ q.answer }}</b>' is consistent with the uppercase option letters
and the data-answer grading attribute.

Adds regression test test_lowercase_answer_normalized_for_grading.

Co-Authored-By: Claude <noreply@anthropic.com>
Thin typer wrapper around studylib.render_html.render_quiz_html:
reads a manifest JSON via ioutils.read_json, renders the interactive
HTML quiz, and writes to --out (default <manifest>.html).

Co-Authored-By: Claude <noreply@anthropic.com>
- studylib/render_paper.py: manifest_to_markdown(variant) + markdown_to_pdf(fonts_dir)
  with CID-fallback font strategy (no font files needed for default path).
- scripts/render_paper.py: CLI --manifest [--variant questions|answers|both]
  [--out-dir] [--fonts-dir].
- requirements.txt: +reportlab>=4.0.
- tests/test_render_paper.py: pure-function + PDF smoke (CID fallback).
- CHANGELOG: F3 entry; remove scripts/md_to_pdf.py (folded into render_paper).

Co-Authored-By: Claude <noreply@anthropic.com>
Implements weighted sampling without replacement via the
Efraimidis-Spirakis keyed method (key = u**(1/w), take top-k):
- syllabus mode weights by exam_weight
- diagnostic mode weights by WEAKNESS_SCORE when any KC has a learning
  record, falling back to exam_weight when all KCs are unseen
- result is deterministic given seed; count is capped to available KCs;
  unknown modes raise ValueError

Co-Authored-By: Claude <noreply@anthropic.com>
… not certainty

Replace the pinned seed=0 assertion (which claimed select_kcs returns
["weak"] with certainty) with a multi-seed distribution test. The
Efraimidis-Spirakis sampler only guarantees higher probability for
higher weights, not certainty — the old test was deterministic but
dishonest. The new test sweeps 400 seeds and asserts the frequency
ordering weak > unseen > confirmed that the weights (1.0/0.4/0.0)
actually imply, plus a >200 majority threshold for weak (observed 274,
theoretical ~284).

Also drop the unused `from studylib.nextstep import WEAKNESS_SCORE`
module-level import. Production code (scripts/studylib/drill.py)
untouched — test-honesty fix only.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
A question tagged with N selected KCs was placed into N by_kc buckets,
then emitted once per bucket by the round-robin, producing duplicate
stems in the drill output. Track seen question_ids during round-robin
and skip already-picked questions while still advancing the slot.

Co-Authored-By: Claude <noreply@anthropic.com>
…outing

Co-Authored-By: Claude <noreply@anthropic.com>
C1: render_quiz_html now uses Environment(autoescape=True); user-controlled
stem/option/solution text can no longer inject raw HTML. Verified by
test_user_text_is_html_escaped.

Minors:
- test: 2-question distinct radio groups (name="q1"/name="q2")
- render_paper: drop unused Spacer/Table/TableStyle/PageBreak imports
- test: assert %PDF- magic bytes in PDF smoke test
- drill: simplify w > 0 guard in _weighted_sample_no_replace
- drill: clarify select_kcs diagnostic comment
- drill CLI: mode-aware summary header (按考纲权重 / 按弱点(自适应))

Co-Authored-By: Claude <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 21, 2026 02:32

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a V2 “drill output” pipeline: selecting KCs (syllabus-weighted or diagnostic-adaptive), gathering questions, building a render-friendly manifest, and rendering either an interactive HTML quiz page or printable PDFs—while standardizing user-facing KC display as kc_id(中文名).

Changes:

  • Introduces drill selection + question gathering (studylib.drill) and a one-stop scripts/drill.py CLI that outputs HTML/PDF/Markdown plus a manifest.
  • Adds a manifest contract (studylib.manifest) and two renderers: HTML (studylib.render_html + templates/quiz.html.j2) and PDF (studylib.render_paper, ReportLab).
  • Unifies KC display via studylib.display.kc_label across next-step, dashboard, evidence, misconception outputs; adds read_json utility and updates changelog/requirements.

Reviewed changes

Copilot reviewed 28 out of 29 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/test_render_paper.py Adds coverage for paper markdown variants + PDF generation smoke test.
tests/test_render_html.py Adds coverage for option parsing, HTML rendering, escaping, grading attributes.
tests/test_nextstep.py Verifies next-step includes kc_label while keeping back-compat fields.
tests/test_manifest.py Adds tests for manifest shape, meta fields, and kc_labels.
tests/test_ioutils.py Adds tests for read_json roundtrip and missing-file behavior.
tests/test_drill_select.py Tests KC selection behavior (modes, determinism, distribution).
tests/test_drill_gather.py Tests question gathering, shortfall, de-duplication.
tests/test_display.py Tests kc_label formatting and fallbacks.
tests/test_cli_smoke.py Extends CLI smoke tests for label output, HTML rendering, and drill CLI.
templates/quiz.html.j2 Adds self-contained interactive quiz HTML template with reveal/grade UX.
templates/dashboard.md.j2 Switches dashboard next-step display to prefer kc_label.
SKILL.md Updates skill routing to require KC label format and adds drill intent routing.
scripts/studylib/render_paper.py Adds ReportLab-based manifest→PDF pipeline with CJK font fallback.
scripts/studylib/render_html.py Adds manifest→HTML rendering, option parsing, and answer normalization.
scripts/studylib/nextstep.py Emits kc_label in next-step recommendations.
scripts/studylib/manifest.py Adds manifest builder that strips unneeded fields and computes kc_labels.
scripts/studylib/ioutils.py Adds read_json helper for JSON files.
scripts/studylib/drill.py Adds KC selection + question gathering utilities for drill.
scripts/studylib/display.py Adds kc_label helper for consistent user-facing KC formatting.
scripts/studylib/dashboard.py Uses kc_label in risk summaries.
scripts/studylib/cli_common.py CLI next-step output now prefers kc_label.
scripts/render_quiz_html.py Adds CLI to render quiz HTML from a manifest JSON.
scripts/render_paper.py Adds CLI to render PDFs from a manifest JSON (questions/answers/both).
scripts/misconception.py Shows KC labels by loading .study/kc.json and using kc_label.
scripts/md_to_pdf.py Removes legacy one-off PDF renderer (superseded by render_paper).
scripts/evidence.py Uses kc_label in evidence listing header and empty-state messages.
scripts/drill.py Adds end-to-end drill CLI (select→gather→manifest→render→summary).
requirements.txt Adds reportlab>=4.0 dependency for PDF rendering.
CHANGELOG.md Records new V2 features and CLI/rendering additions under Unreleased/rc1.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +34 to +35
ans = q.get("answer", "") or ""
letters = sorted({unicodedata.normalize("NFKC", ch).upper() for ch in ans if ch.strip()})
Comment thread scripts/studylib/drill.py
Comment on lines +67 to +68
shortfall = {k: per_kc - len(by_kc[k]) for k in kc_ids if len(by_kc[k]) < per_kc}
return picked, shortfall
@TDXu TDXu closed this Jul 21, 2026
@TDXu
TDXu deleted the feat/v2-display-drill-output branch July 21, 2026 14:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants