Phase 8: Customizable PDF Templates — locale support, admin editor, seed templates
Backend: - PdfTemplate model with locale field + UNIQUE(name, locale) constraint - Migration 007: pdf_templates table + template_id FK on generated_pdfs - Template service: CRUD, Jinja2 validation, render preview with sample data - Admin endpoints: CRUD /admin/pdf-templates + POST preview - User endpoint: GET /pdf/templates (active templates list) - pdf_service: resolves template from DB by ID or falls back to default for the appropriate locale - AI generate_pdf tool accepts optional template_id - Seed script + 4 HTML template files: - Basic Report (en/ru) — general-purpose report - Medical Report (en/ru) — health-focused with disclaimers Frontend: - Admin PDF templates page with editor, locale selector, live preview (iframe), template variables reference panel - PDF page: template selector dropdown in generation form - API clients for admin CRUD + user template listing - Sidebar: admin templates link - English + Russian translations Also added Phase 9 (OAuth) and Phase 10 (Rate Limits) placeholders to GeneralPlan. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
51
backend/scripts/seed_templates.py
Normal file
51
backend/scripts/seed_templates.py
Normal file
@@ -0,0 +1,51 @@
|
||||
"""Seed default PDF templates (basic + medical, en + ru)."""
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
|
||||
from sqlalchemy import select
|
||||
|
||||
from app.database import async_session_factory
|
||||
from app.models.pdf_template import PdfTemplate
|
||||
|
||||
TEMPLATES_DIR = Path(__file__).parent.parent / "app" / "templates" / "pdf" / "seeds"
|
||||
|
||||
|
||||
async def seed_templates() -> None:
|
||||
templates = [
|
||||
("Basic Report", "en", "Standard report with key information and documents", "basic_en.html", True),
|
||||
("Basic Report", "ru", "Стандартный отчёт с ключевой информацией и документами", "basic_ru.html", True),
|
||||
("Medical Report", "en", "Medical-styled report with health profile and disclaimers", "medical_en.html", False),
|
||||
("Medical Report", "ru", "Медицинский отчёт с профилем здоровья и дисклеймером", "medical_ru.html", False),
|
||||
]
|
||||
|
||||
async with async_session_factory() as db:
|
||||
for name, locale, desc, filename, is_default in templates:
|
||||
result = await db.execute(
|
||||
select(PdfTemplate).where(PdfTemplate.name == name, PdfTemplate.locale == locale)
|
||||
)
|
||||
if result.scalar_one_or_none():
|
||||
print(f" Exists: {name} ({locale})")
|
||||
continue
|
||||
|
||||
html_path = TEMPLATES_DIR / filename
|
||||
if not html_path.exists():
|
||||
print(f" Missing: {html_path}")
|
||||
continue
|
||||
|
||||
html_content = html_path.read_text(encoding="utf-8")
|
||||
template = PdfTemplate(
|
||||
name=name,
|
||||
locale=locale,
|
||||
description=desc,
|
||||
html_content=html_content,
|
||||
is_default=is_default,
|
||||
)
|
||||
db.add(template)
|
||||
print(f" Created: {name} ({locale})")
|
||||
|
||||
await db.commit()
|
||||
print("Template seeding complete.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(seed_templates())
|
||||
Reference in New Issue
Block a user