Spaces:
Running
Running
| """Document generation service via docxtpl (.docx + Jinja2).""" | |
| from __future__ import annotations | |
| import io | |
| from datetime import datetime | |
| from pathlib import Path | |
| from docxtpl import DocxTemplate | |
| from loguru import logger | |
| from app.models.tech_stack import DocumentRequest | |
| TEMPLATES_DIR = Path(__file__).resolve().parents[2] / "templates" | |
| class DocumentEngine: | |
| """Render .docx templates with supplied context.""" | |
| SUPPORTED = { | |
| "act_hidden_works": "act_hidden_works.docx", | |
| "act_osmotr_responsibility": "act_osmotr_responsibility.docx", | |
| "general_work_log": "general_work_log.docx", | |
| "material_certificate": "material_certificate.docx", | |
| } | |
| def render(self, request: DocumentRequest) -> bytes: | |
| template_file = self.SUPPORTED.get(request.template_name) | |
| if template_file is None: | |
| raise ValueError( | |
| f"Unknown template: {request.template_name}. " | |
| f"Supported: {list(self.SUPPORTED)}" | |
| ) | |
| template_path = TEMPLATES_DIR / template_file | |
| if not template_path.exists(): | |
| raise FileNotFoundError( | |
| f"Template file not found: {template_path}" | |
| ) | |
| logger.info(f"Rendering document: {request.template_name}") | |
| tpl = DocxTemplate(str(template_path)) | |
| context = { | |
| "generated_at": datetime.utcnow().strftime("%d.%m.%Y %H:%M"), | |
| "project_id": request.project_id, | |
| **request.context, | |
| } | |
| tpl.render(context) | |
| buf = io.BytesIO() | |
| tpl.save(buf) | |
| buf.seek(0) | |
| return buf.read() | |
| def get_document_engine() -> DocumentEngine: | |
| return DocumentEngine() | |