Files
homework-template/scripts/gen_index.py
2026-04-08 13:55:17 +08:00

157 lines
4.6 KiB
Python

#!/usr/bin/env python3
"""
gen_index.py - Generate index.typ from questions.json.
This script generates an index.typ file that includes all questions
and their answers in the specified format.
"""
import argparse
import sys
from pathlib import Path
from rich.console import Console
from common import DATA_DIR, setup_logging
console = Console()
def load_questions_json() -> list[dict]:
"""Load questions from questions.json."""
questions_path = DATA_DIR / "questions.json"
if not questions_path.exists():
console.print(f"[red]Error: questions.json not found at {questions_path}[/red]")
sys.exit(1)
import json
with open(questions_path, "r", encoding="utf-8") as f:
return json.load(f)
def read_typ_content(target: str) -> str | None:
"""Read typ file content."""
typ_path = DATA_DIR / target
if not typ_path.exists():
console.print(f"[yellow]Warning: {target} not found[/yellow]")
return None
try:
return typ_path.read_text(encoding="utf-8")
except Exception as e:
console.print(f"[yellow]Warning: Failed to read {target}: {e}[/yellow]")
return None
def indent_text(text: str, indent: int = 2) -> str:
"""Indent text by specified spaces."""
lines = text.strip().split("\n")
spaces = " " * indent
return "\n".join(spaces + line if line.strip() else "" for line in lines)
def generate_index(questions: list[dict], dry_run: bool, logger) -> str:
"""Generate index.typ content."""
lines = [
'#import "@local/phomework:0.1.0": homework, question, answer, shadow',
"",
]
enable_shadow = (
"true"
if any(DATA_DIR / f"A_{q['question']}.md" for q in questions)
else "false"
)
lines.append(
f'#homework(title: "计算机网络第三次作业", secret: read(".secret"), enable_shadow: {enable_shadow})['
)
def sort_key(q: dict) -> tuple:
name = q["question"]
prefix = name[0]
try:
num = int(name[1:])
except ValueError:
num = float("inf")
return (0 if prefix == "R" else 1, prefix, num)
sorted_questions = sorted(questions, key=sort_key)
for q in sorted_questions:
question_name = q["question"]
typ_target = q["target"]
lines.append(f' #question(title: "{question_name}")[')
content = read_typ_content(typ_target)
if content:
lines.append(indent_text(content, 4))
else:
lines.append(" [题目内容加载失败]")
lines.append(" ]")
lines.append("")
lines.append(" #answer[")
answer_file = DATA_DIR / f"A_{question_name}.md"
if answer_file.exists():
lines.append(" 请填写答案。")
lines.append("")
lines.append(f' #shadow(read("./data/A_{question_name}.md"))')
else:
lines.append(" [答案文件不存在]")
lines.append(" ]")
lines.append("]")
return "\n".join(lines) + "\n"
def parse_args() -> argparse.Namespace:
"""Parse command line arguments."""
parser = argparse.ArgumentParser(
description="Generate index.typ from questions.json"
)
parser.add_argument("--dry-run", action="store_true", help="Do not write files")
parser.add_argument(
"--force", action="store_true", help="Force overwrite without warning"
)
parser.add_argument("--verbose", action="store_true", help="Enable debug logging")
return parser.parse_args()
def main() -> None:
"""Main entry point."""
args = parse_args()
logger = setup_logging("gen_index", args.verbose)
questions = load_questions_json()
logger.info(f"Loaded {len(questions)} questions")
if not questions:
console.print("[yellow]No questions found in questions.json[/yellow]")
sys.exit(1)
content = generate_index(questions, args.dry_run, logger)
output_path = Path("index.typ")
if output_path.exists() and not args.force and not args.dry_run:
console.print(
f"[yellow]Warning: {output_path} already exists and will be overwritten![/yellow]"
)
response = input("Continue? [y/N]: ")
if response.lower() != "y":
console.print("[yellow]Aborted.[/yellow]")
sys.exit(0)
if args.dry_run:
logger.info(f"[DRY-RUN] Would write {output_path}")
logger.debug(f"Content preview:\n{content[:500]}...")
else:
output_path.write_text(content, encoding="utf-8")
logger.info(f"Wrote {output_path}")
console.print(f"[green]Successfully wrote {output_path}[/green]")
if __name__ == "__main__":
main()