| 1 | from pathlib import Path
|
|---|
| 2 | from jinja2 import Environment, FileSystemLoader
|
|---|
| 3 | from builder.exam import Exam
|
|---|
| 4 |
|
|---|
| 5 |
|
|---|
| 6 | class ExamGenerator:
|
|---|
| 7 | """Encapsulates exam rendering and export logic."""
|
|---|
| 8 |
|
|---|
| 9 | def __init__(self, template_dir: Path, media_dir: Path, static_dir: Path, output_dir: Path):
|
|---|
| 10 | self.env = Environment(
|
|---|
| 11 | loader=FileSystemLoader(template_dir),
|
|---|
| 12 | autoescape=True,
|
|---|
| 13 | )
|
|---|
| 14 | self.media_dir = media_dir
|
|---|
| 15 | self.static_dir = static_dir
|
|---|
| 16 | self.output_dir = output_dir
|
|---|
| 17 | # make getattr available in templates
|
|---|
| 18 | self.env.globals.update(getattr=self._getattr_or_item)
|
|---|
| 19 |
|
|---|
| 20 | @staticmethod
|
|---|
| 21 | def _getattr_or_item(obj, name):
|
|---|
| 22 | if isinstance(obj, dict):
|
|---|
| 23 | return obj.get(name)
|
|---|
| 24 | return getattr(obj, name)
|
|---|
| 25 |
|
|---|
| 26 | def render_exam(self, exam: Exam) -> str:
|
|---|
| 27 | template = self.env.get_template("exam_template.jinja2")
|
|---|
| 28 | return template.render(exam=exam)
|
|---|
| 29 |
|
|---|
| 30 | def write_exam(self, exam: Exam, html: str):
|
|---|
| 31 | return exam.to_zip(html, self.media_dir, self.static_dir / "exam.css", self.output_dir)
|
|---|
| 32 |
|
|---|
| 33 | def generate_from_json(self, json_file: Path) -> Path:
|
|---|
| 34 | exam = Exam.from_json_file(json_file)
|
|---|
| 35 | exam.shuffle_options()
|
|---|
| 36 | html = self.render_exam(exam)
|
|---|
| 37 | return self.write_exam(exam, html)
|
|---|