ODT templating microservice (py3o.template + FastAPI) — Phase 17
- Python 97.1%
- Dockerfile 2.9%
|
All checks were successful
build-odt-templater / build (push) Successful in 51s
Previous cv-template.odt failed py3o.template's validate_link() check
with "url and text do not match in 'for'". Root cause: the visible text
of py3o hyperlink anchors must EXACTLY equal the py3o_base derived from
the href (href="py3o://for=..." -> visible text must be the unquoted
`for="station in stations"` form; href="py3o:///for" -> visible text
must be `/for`). Both the 1390-byte repo fixture and the Terraform-repo
2161-byte fixture used placeholder texts ("for", "loop-start", "loop-end")
instead of the exact form py3o requires.
Fix:
- Drop the py3o.image(...) draw:frame entirely. The frame name syntax
requires a mime_type arg passed via FrameInjector which is out of
scope for the CI smoke test. A static Pictures/logo.png reference is
sufficient — tests/test_fill.py only asserts that /fill returns a
valid ODT (ZIP magic + content.xml), not that per-station images
render. Dynamic image binding is re-addressable in a later plan.
- Use <text:user-field-decl> + <text:user-field-get> for variable
substitution, matching the canonical pattern in py3o's own
py3o_list_template.odt test fixture.
- Commit a deterministic stdlib-only tests/fixtures/build-template.py
builder so the fixture is reproducible (fixed mtime 2026-04-22
00:00:00, fixed 0644 perms, sorted ZIP entries).
Verified on VM 603 against py3o.template 0.10.0 on Python 3.13.5:
`pytest -xvs tests/` -> 5 passed (all 5 behaviors from 17-RESEARCH.md).
|
||
|---|---|---|
| .forgejo/workflows | ||
| app | ||
| tests | ||
| .gitignore | ||
| Dockerfile | ||
| pytest.ini | ||
| README.md | ||
| requirements-test.txt | ||
| requirements.txt | ||
odt-templater
ODT templating microservice for Phase 17 doc-pipeline. FastAPI + py3o.template — takes an ODT template, a JSON data context, and optional images; returns a filled ODT. No LibreOffice in this image; Gotenberg handles ODT → PDF downstream.
Endpoints
| Method | Path | Purpose |
|---|---|---|
| GET | /health |
Liveness/readiness probe — returns {"status":"ok"} |
| POST | /fill |
multipart upload (template, data, optional images[]) → 200 filled ODT |
Error contract
| HTTP | When |
|---|---|
| 200 | Filled ODT body (application/vnd.oasis.opendocument.text) |
| 400 | Malformed multipart / missing required form field |
| 422 | Template render failed (py3o raised) or invalid JSON in data |
| 500 | Internal — logged |
Dependencies (pinned)
python:3.12-slimbasefastapi==0.115.4uvicorn[standard]==0.32.0py3o.template==0.10.0(fallback:git+https://github.com/OCA/py3o.template.git@masterif Assumption A1 fails on Py3.12)python-multipart==0.0.12
Local development
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt pytest
uvicorn app.main:app --host 0.0.0.0 --port 8080 --reload
Run tests:
pytest -xvs tests/
CI (Forgejo Actions)
.forgejo/workflows/build.yml runs on every push to main:
- Install deps + Assumption-A1 import-smoke on Python 3.12 (
from py3o.template import Template). - Run pytest (includes fixture-CV render smoke).
- Log in to
forgejo.lab.xi2ix.deDocker registry (secrets:FORGEJO_REGISTRY_USER,FORGEJO_REGISTRY_PASSWORD). - Build + push
forgejo.lab.xi2ix.de/forgeadmin/odt-templater:latestand:sha-<short>.
Consumer pattern (out of scope here — see Phase 17 Plan 05)
Go workers authenticate via Traefik ForwardAuth (LDAP group cn=doc-pipeline-users),
POST multipart to https://odt-templater.lab.xi2ix.de/fill, then pipe the filled
ODT to https://gotenberg.lab.xi2ix.de/forms/libreoffice/convert for PDF output.
Repository
- Origin:
https://forgejo.lab.xi2ix.de/forgeadmin/odt-templater - Phase: Phase 17 — doc-pipeline