ODT templating microservice (py3o.template + FastAPI) — Phase 17
  • Python 97.1%
  • Dockerfile 2.9%
Find a file
forgeadmin b22a7fc2b4
All checks were successful
build-odt-templater / build (push) Successful in 51s
fix(17-02): rebuild cv-template.odt as py3o-valid fixture
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).
2026-04-22 01:40:13 +02:00
.forgejo/workflows fix(17-02): add httpx test dependency to unblock pytest collection 2026-04-22 01:27:46 +02:00
app feat(odt-templater): initial FastAPI + py3o service with CI 2026-04-21 12:08:50 +02:00
tests fix(17-02): rebuild cv-template.odt as py3o-valid fixture 2026-04-22 01:40:13 +02:00
.gitignore feat(odt-templater): initial FastAPI + py3o service with CI 2026-04-21 12:08:50 +02:00
Dockerfile feat(odt-templater): initial FastAPI + py3o service with CI 2026-04-21 12:08:50 +02:00
pytest.ini fix(17-02): add pytest.ini with pythonpath=. to resolve app import 2026-04-22 01:29:00 +02:00
README.md feat(odt-templater): initial FastAPI + py3o service with CI 2026-04-21 12:08:50 +02:00
requirements-test.txt fix(17-02): add httpx test dependency to unblock pytest collection 2026-04-22 01:27:46 +02:00
requirements.txt feat(odt-templater): initial FastAPI + py3o service with CI 2026-04-21 12:08:50 +02:00

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-slim base
  • fastapi==0.115.4
  • uvicorn[standard]==0.32.0
  • py3o.template==0.10.0 (fallback: git+https://github.com/OCA/py3o.template.git@master if 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:

  1. Install deps + Assumption-A1 import-smoke on Python 3.12 (from py3o.template import Template).
  2. Run pytest (includes fixture-CV render smoke).
  3. Log in to forgejo.lab.xi2ix.de Docker registry (secrets: FORGEJO_REGISTRY_USER, FORGEJO_REGISTRY_PASSWORD).
  4. Build + push forgejo.lab.xi2ix.de/forgeadmin/odt-templater:latest and :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