From 22b05a7a1b8ae58c668d29cf56db663798ed1b97 Mon Sep 17 00:00:00 2001 From: rabenNest7$ Date: Wed, 24 Jun 2026 14:31:42 +0000 Subject: [PATCH] first commit --- .env.example | 5 ++ .gitignore | 7 +++ LICENSE | 17 +++++++ MANIFEST.txt | 92 +++++++++++++++++++++++++++++++++ Makefile | 27 ++++++++++ QUICKSTART.md | 52 +++++++++++++++++++ README.md | 124 +++++++++++++++++++++++++++++++++++++++++++++ SHA256SUMS.txt | 92 +++++++++++++++++++++++++++++++++ docker-compose.yml | 38 ++++++++++++++ pytest.ini | 2 + 10 files changed, 456 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 MANIFEST.txt create mode 100644 Makefile create mode 100644 QUICKSTART.md create mode 100644 SHA256SUMS.txt create mode 100644 docker-compose.yml create mode 100644 pytest.ini diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..10aa9d9 --- /dev/null +++ b/.env.example @@ -0,0 +1,5 @@ +TZ=Europe/Berlin +REPORT_RENDERER_PORT=8080 +GOTENBERG_URL=http://gotenberg:3000 +RENDER_TIMEOUT_SECONDS=60 +MAX_PAYLOAD_BYTES=2097152 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..428b102 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.env +__pycache__/ +*.pyc +.pytest_cache/ +output/*.pdf +output/*.html +!output/.gitkeep diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d8c1899 --- /dev/null +++ b/LICENSE @@ -0,0 +1,17 @@ +MIT License + +Copyright (c) 2026 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. diff --git a/MANIFEST.txt b/MANIFEST.txt new file mode 100644 index 0000000..fb84bbe --- /dev/null +++ b/MANIFEST.txt @@ -0,0 +1,92 @@ +.env.example +.gitignore +LICENSE +Makefile +QUICKSTART.md +README.md +docker-compose.yml +docs/CUSTOMIZATION.md +docs/DATA_MODEL.md +docs/PRODUCTION_CHECKLIST.md +examples/curl-render.sh +examples/render-all-themes.sh +examples/sample-claude-input.json +examples/sample-claude-output.json +examples/sample-report.json +n8n/README.md +n8n/code-build-claude-request.js +n8n/code-build-render-payload.js +n8n/code-extract-claude-output.js +n8n/code-normalize-month.js +n8n/postgres-example-queries.sql +n8n/postgres-schema.sql +n8n/workflow-template.json +output/.gitkeep +output/sample-report-classic.html +output/sample-report-classic.pdf +output/sample-report-modern.html +output/sample-report-modern.pdf +output/theme-catalog/alpine-sage.html +output/theme-catalog/classic.html +output/theme-catalog/editorial-ink.html +output/theme-catalog/editorial-ivory.html +output/theme-catalog/editorial-rose.html +output/theme-catalog/executive-blue.html +output/theme-catalog/executive-burgundy.html +output/theme-catalog/executive-charcoal.html +output/theme-catalog/forest-lodge.html +output/theme-catalog/ledger-blue.html +output/theme-catalog/ledger-green.html +output/theme-catalog/ledger-sepia.html +output/theme-catalog/magazine-citrus.html +output/theme-catalog/magazine-cobalt.html +output/theme-catalog/magazine-plum.html +output/theme-catalog/midnight-gold.html +output/theme-catalog/modern.html +output/theme-catalog/nordic-light.html +output/theme-catalog/nordic-night.html +output/theme-catalog/ocean-breeze.html +output/theme-catalog/poster-mono.html +output/theme-catalog/poster-red.html +output/theme-catalog/poster-yellow.html +output/theme-catalog/soft-lavender.html +output/theme-catalog/split-teal.html +output/theme-catalog/split-terracotta.html +output/theme-catalog/split-violet.html +output/theme-catalog/sunset-coral.html +output/theme-catalog/theme-gallery.html +output/theme-catalog/theme-gallery.pdf +prompts/anthropic-request-template.json +prompts/system-prompt.txt +prompts/user-prompt-template.txt +pytest.ini +renderer/Dockerfile +renderer/app/__init__.py +renderer/app/formatting.py +renderer/app/main.py +renderer/app/renderer.py +renderer/app/schemas/claude-output-anthropic.schema.json +renderer/app/schemas/claude-output.schema.json +renderer/app/schemas/report-payload.schema.json +renderer/app/templates/_shared/footer.html.j2 +renderer/app/templates/_shared/macros.html.j2 +renderer/app/templates/classic/footer.html.j2 +renderer/app/templates/classic/report.html.j2 +renderer/app/templates/dashboard/report.html.j2 +renderer/app/templates/editorial/report.html.j2 +renderer/app/templates/ledger/report.html.j2 +renderer/app/templates/magazine/report.html.j2 +renderer/app/templates/minimal/report.html.j2 +renderer/app/templates/modern/footer.html.j2 +renderer/app/templates/modern/report.html.j2 +renderer/app/templates/poster/report.html.j2 +renderer/app/templates/sidebar/report.html.j2 +renderer/app/templates/split/report.html.j2 +renderer/app/theme_catalog.py +renderer/app/validation.py +renderer/requirements.txt +scripts/render-local.py +scripts/render-theme-gallery.py +tests/conftest.py +tests/test_render.py +tests/test_validation.py diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..64c4c14 --- /dev/null +++ b/Makefile @@ -0,0 +1,27 @@ +.PHONY: up down logs test sample preview zip + +up: + docker compose up -d --build + +down: + docker compose down + +logs: + docker compose logs -f report-renderer gotenberg + +test: + python -m pytest -q + +sample: + mkdir -p output + curl -sS -X POST http://localhost:8080/render \ + -H 'Content-Type: application/json' \ + --data-binary @examples/sample-report.json \ + -o output/sample-report.pdf + +preview: + mkdir -p output + curl -sS -X POST http://localhost:8080/preview/html \ + -H 'Content-Type: application/json' \ + --data-binary @examples/sample-report.json \ + -o output/sample-report.html diff --git a/QUICKSTART.md b/QUICKSTART.md new file mode 100644 index 0000000..8d0211c --- /dev/null +++ b/QUICKSTART.md @@ -0,0 +1,52 @@ +# Schnellstart in 10 Schritten + +1. Ordner auf deinen Docker-Host kopieren. +2. `.env.example` nach `.env` kopieren. +3. `docker compose up -d --build` starten. +4. `http://HOST:8080/health` pruefen. +5. `examples/sample-report.json` anpassen. +6. Mit `/preview/html` zuerst das HTML kontrollieren. +7. Mit `/render` das PDF erzeugen. +8. `n8n/workflow-template.json` in n8n importieren. +9. Anthropic-API-Key in n8n als Credential oder Secret hinterlegen. +10. Beispiel-Set-Node durch deine PostgreSQL-Abfragen ersetzen. + +## Minimaler Render-Aufruf + +```bash +curl -sS \ + -X POST http://localhost:8080/render \ + -H 'Content-Type: application/json' \ + --data-binary @examples/sample-report.json \ + -o output/report.pdf +``` + +## Dienste + +- Report Renderer: `http://localhost:8080` +- Gotenberg: standardmaessig nur im internen Docker-Netz erreichbar + +## API-Routen + +- `GET /health` +- `GET /themes` +- `POST /validate` +- `POST /preview/html` +- `POST /render` + + +## Designs ansehen + +Alle IDs abfragen: + +```bash +curl -s http://localhost:8080/themes | python3 -m json.tool +``` + +Im Beispiel-Payload `design.theme` ändern oder alle Designs rendern: + +```bash +bash examples/render-all-themes.sh +``` + +Der mitgelieferte visuelle Katalog liegt unter `output/theme-catalog/theme-gallery.pdf`. diff --git a/README.md b/README.md index e69de29..132e24b 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,124 @@ +# Monthly Report Kit + +Ein reproduzierbares Starterpaket fuer monatliche Kundenreports mit: + +- Claude Structured Outputs fuer valide, schemakonforme Inhalte +- fester HTML/CSS-Vorlage statt KI-generiertem Layout +- 28 austauschbaren Designs in 10 Layoutfamilien +- Python/FastAPI-Render-Service +- Gotenberg fuer die PDF-Erzeugung mit Chromium +- JSON-Schema-Validierung vor jedem Rendern +- n8n-Bausteinen und einem importierbaren Beispielworkflow +- Beispielreport, Prompt-Vorlagen und PostgreSQL-Grundstruktur + +## Architektur + +```text +PostgreSQL / andere Datenquelle + | + v +n8n normalisiert Monatsdaten + | + v +Claude Messages API + output_config.format + | + v +validiertes Claude-JSON + | + v +Report-Payload aus Kunde + Kennzahlen + Claude-Inhalt + | + v +report-renderer -> HTML/Jinja2 -> Gotenberg -> PDF +``` + +Claude entscheidet nur, **was** im Report steht. Das Template entscheidet, **wie** es aussieht. + +## Schnellstart + +```bash +cp .env.example .env +docker compose up -d --build +curl http://localhost:8080/health +``` + +Beispielreport erzeugen: + +```bash +curl \ + --request POST http://localhost:8080/render \ + --header 'Content-Type: application/json' \ + --data @examples/sample-report.json \ + --output output/monatsreport-beispiel.pdf +``` + +HTML-Vorschau im Browser: + +```bash +curl \ + --request POST http://localhost:8080/preview/html \ + --header 'Content-Type: application/json' \ + --data @examples/sample-report.json \ + --output output/preview.html +``` + +## Wichtigste Dateien + +| Datei | Zweck | +|---|---| +| `renderer/app/theme_catalog.py` | Katalog aller 28 Designs und Farbpaletten | +| `renderer/app/templates/` | 10 wiederverwendbare Layoutfamilien | +| `scripts/render-theme-gallery.py` | erzeugt Vorschauen und einen PDF-Designkatalog | +| `renderer/app/schemas/claude-output-anthropic.schema.json` | API-kompatibles Structured-Output-Schema | +| `renderer/app/schemas/claude-output.schema.json` | strenge Nachvalidierung mit Textlaengen und Grenzen | +| `renderer/app/schemas/report-payload.schema.json` | kompletter Render-Payload | +| `prompts/system-prompt.txt` | feste Claude-Rolle und Regeln | +| `prompts/user-prompt-template.txt` | dynamischer Auftrag je Kunde/Monat | +| `n8n/workflow-template.json` | importierbarer Workflow-Startpunkt | +| `n8n/postgres-schema.sql` | Tabellen fuer Konfiguration und Reportlaeufe | + +## Design auswählen + +Alle verfügbaren Designs liefert der Renderer über `GET /themes`. Im Payload genügt die Theme-ID: + +```json +{ + "design": { + "theme": "executive-blue", + "palette_mode": "theme" + } +} +``` + +`palette_mode: "theme"` verwendet die fest definierte Farbwelt des Designs. Mit `palette_mode: "custom"` bleiben Layout und Typografie erhalten, während die im Payload gelieferten Kundenfarben verwendet werden. Die flexiblen Varianten `modern` und `classic` verwenden standardmäßig Kundenfarben. + +### Enthaltene Layoutfamilien + +| Familie | Charakter | Designs | +|---|---|---| +| Modern | Karten, Verlaufskopf, weich | `modern`, `soft-lavender`, `midnight-gold` | +| Classic | Serifenschrift, feine Linien | `classic` | +| Minimal | viel Weißraum, ruhige Flächen | `nordic-light`, `nordic-night`, `alpine-sage` | +| Dashboard | Management-orientierte KPI-Flächen | `executive-blue`, `executive-charcoal`, `executive-burgundy` | +| Sidebar | markante Seitenleiste | `ocean-breeze`, `forest-lodge`, `sunset-coral` | +| Editorial | magazinartige Serifengestaltung | `editorial-ivory`, `editorial-ink`, `editorial-rose` | +| Magazine | große Typografie, asymmetrische Module | `magazine-cobalt`, `magazine-citrus`, `magazine-plum` | +| Ledger | dicht, tabellarisch, zahlenorientiert | `ledger-green`, `ledger-blue`, `ledger-sepia` | +| Split | geteilte Titel- und Inhaltsflächen | `split-teal`, `split-violet`, `split-terracotta` | +| Poster | plakativ, sehr große Kennzahlen | `poster-red`, `poster-yellow`, `poster-mono` | + +Den vollständigen visuellen Katalog findest du nach dem Entpacken unter `output/theme-catalog/theme-gallery.pdf`. Neu erzeugen kannst du ihn mit: + +```bash +python scripts/render-theme-gallery.py +``` + +## Produktionshinweise + +- API-Key niemals in Workflows exportieren oder im Repository speichern. +- `report_version`, `prompt_version` und `template_version` je Report sichern. +- Vor dem Versand immer Payload- und PDF-Pruefungen ausfuehren. +- Wiederholungen ueber einen eindeutigen Schluessel aus `customer_id + period` verhindern. +- Externe Bild-URLs vermeiden. Logos als Base64 Data URI uebergeben. + +Weitere Hinweise stehen unter `docs/`. diff --git a/SHA256SUMS.txt b/SHA256SUMS.txt new file mode 100644 index 0000000..0574cf8 --- /dev/null +++ b/SHA256SUMS.txt @@ -0,0 +1,92 @@ +3772de64cc98a7a239dd0ac1b671f5727c436eb97fdb6c04e4a026ac7c927d5e .env.example +cddeca481da53748d012f440fb4cc6346d79a4a435b088790df22a8fbeb5d14e .gitignore +ec4ab498897ee0d12aa7f8e354f3a7d4fd1baaf9db18dbf99927ffb78bed08ed LICENSE +734d0a6525a8303825860336d7199dd3a1f0c64ca6eef6f5216028b9c1de410d Makefile +f89b8af8904b9c2db9d798faa666e3dd680c1b1bb07dedde707a2db802780c15 QUICKSTART.md +8a173a2995256f0a31633d5bb7a389701b7ce7c0ed8f4b6bc75bb8ea77f33cec README.md +0234c23e00d70cd029f88c3d720326ec3fe0d14d4b857e36bb191836cece95d5 docker-compose.yml +339d856cdafc72a681fc14f5b21c525852ed7f9157e70464a21978328a8754a0 docs/CUSTOMIZATION.md +86bb53e3a4c40bf29feb11af0d4e160365b323010bdb846f473838afb66c5a4d docs/DATA_MODEL.md +9de8e978680517af375577c166af6fee0698a9e86ce0e37cd877dc2b91971b59 docs/PRODUCTION_CHECKLIST.md +901e0b2ffe44168bcdca34b2f37d257eafd183b50b66ab0fec38698a87014142 examples/curl-render.sh +ca7b0ddae3f0456def13ae40183ecf97e1d718e6fb80382ae2d8995e8260e00e examples/render-all-themes.sh +a188ee2512e5acc5015495e10701b412fb673dfb37885b580a4b2ece983f915f examples/sample-claude-input.json +c5db2c90552fc7499225c96a19f415cadeb40501c7834842c6e24ac8448467bd examples/sample-claude-output.json +9d83b926c4eab21bd7a9468bbde907a4a7075aa0a9f088fec3b587e4b76037c1 examples/sample-report.json +114c245d8200036e65e6e40530b747c643a09ce328e719b6713a0405ea75083f n8n/README.md +d95757c0b89bbb9257833f552f6fa28e5a69998421ce2ddfbf5eea1bdcf4b70a n8n/code-build-claude-request.js +19a764449c5040ac6cd1beee28a6a79073ea33063134b846a59a5bae23c44643 n8n/code-build-render-payload.js +2181b163b2a38bc280099a45fce6ade4729376f299e7b5b29ffbe62697471a7f n8n/code-extract-claude-output.js +e5a3dbef59ddb1b926a1fd282c3cf0f763d54429ed7f5f3265cb560544d663a4 n8n/code-normalize-month.js +c5f095a90926e7909d28623c8e2ae1e4dd2a755597b8f258de46e6040669168e n8n/postgres-example-queries.sql +1a69a0f745ea8b6b0f4aeaf841bfe30efd215dfab4d428dbf94451402a5ff54d n8n/postgres-schema.sql +80a655e305f2a9e6f33b73f7e1b6c468498727237260a49612b51ab137f1a2dc n8n/workflow-template.json +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 output/.gitkeep +67e0aa47dbab19484bd9668105f0c4452ec28295a23ca320b25f1e7067bb92b1 output/sample-report-classic.html +eda43c23832bd4ad9f0b8b6657235a13dd7fe3ccff6d25b8b62efd486ada1cdb output/sample-report-classic.pdf +3b51d683b8b13ba657d71067cb6b3ab5ba007aca4baa4d200bc654dc2e5da9af output/sample-report-modern.html +a4515612d9c153d77b423d6247501be2ecab64777b76ebb28ca9744b9ddfea06 output/sample-report-modern.pdf +182b07aa4da2759c1be79e82e72acefc068c89b1cdd4a79acec319ee76695a23 output/theme-catalog/alpine-sage.html +67e0aa47dbab19484bd9668105f0c4452ec28295a23ca320b25f1e7067bb92b1 output/theme-catalog/classic.html +cd65005a85bbe343a94773f8d97c6f12c75a313d7ea418858e3d4c892ecc744e output/theme-catalog/editorial-ink.html +e9e5e90889cf6b8075b3653d5cfc8632f17004ade3e4a3ec6dda2c0223ebb940 output/theme-catalog/editorial-ivory.html +9f6657791c563288c6a3e2bb0a154cbd356db0a9a33512343738956ae931272e output/theme-catalog/editorial-rose.html +9085f9d73c9afac53e336c424cde1f0fe8b1d7b062864db56eb9e45b5cdbdc46 output/theme-catalog/executive-blue.html +97227d9ed5d70ab9a24fe274e6718db808029cc12c5ffc09b451d8211a3c3031 output/theme-catalog/executive-burgundy.html +64607b1569dc17840cf8fc10a4eccd6409eb084193e46beb8556fbb8bc77fddc output/theme-catalog/executive-charcoal.html +2106baf980e86f5bdd0a520a073df40d35428a215e4efcc7f3f472adcdfa8594 output/theme-catalog/forest-lodge.html +3732f81d77b1cc80dba468f2cbda94779a613630ae74d84c65efcba7ec742c7c output/theme-catalog/ledger-blue.html +78783ddbc17c0e9866b25b6b93fa9010c488610115f4fa1a794108a7fb56e040 output/theme-catalog/ledger-green.html +514e8cf3e981ae304262866edae11cb69628ca870ba6ec7736969c9ca6f2ebe9 output/theme-catalog/ledger-sepia.html +f2b4413e8743a8e36fc5721df8fd3bad8cd4f500ecf60714d346086e34e2cde2 output/theme-catalog/magazine-citrus.html +889db73c6c649bb114fdd29055abc5e0a8fb30fe66e98fefb9608e4d25bdd823 output/theme-catalog/magazine-cobalt.html +2b7911ed20750b1939f96cc030243d95dd8ade9841c4cca3e059524f6e3b7d20 output/theme-catalog/magazine-plum.html +786f9f8ad6e27a27ab7ad0579d4ff5bd37900a653c53519f1aa5c7d9e7de83e6 output/theme-catalog/midnight-gold.html +3b51d683b8b13ba657d71067cb6b3ab5ba007aca4baa4d200bc654dc2e5da9af output/theme-catalog/modern.html +c12b3922f8f96afd221c1e9049fd5eaa7fcca6fdf608221630fe0deed6cfa07f output/theme-catalog/nordic-light.html +7ae6c3eae738abb26f7272e4171ef23dc3ded84e12938a2d35e1b9a5437a70b1 output/theme-catalog/nordic-night.html +52e63e72d056acbbb93f9c9954ffdf9406e18966955f9b3c76c54ef257e14995 output/theme-catalog/ocean-breeze.html +b0ccf9b7e7d492ce27a002b3da5b0aa7f6b056102997375e4492b54af4ba1f73 output/theme-catalog/poster-mono.html +11a3b7ebfc570371b0d2d59e065b0083630bfd5b180a679f045d22fc427fe47b output/theme-catalog/poster-red.html +d456da8ab54fc6b6e4c10d3fb5d10d964db40c819d2f3bb53816a1198315e0dc output/theme-catalog/poster-yellow.html +fe926c94c73f51b1aabf03bafef7a04acda08c6291ac19a70cfe5897ffb2fac8 output/theme-catalog/soft-lavender.html +b96d8a0c74e8f41e19085620366448bd4492ae49bd950268585a76f2a4a8846c output/theme-catalog/split-teal.html +c6dc77a1de12a41cf9540d8906debaf4512cc431c2797eacf524b1df15400924 output/theme-catalog/split-terracotta.html +2472963115164b2ae2c7fb38278e691065db845a95db8ad94be183aef66ae612 output/theme-catalog/split-violet.html +7aba082062da87bd78d08d8d984ee7ca755fe32917654e72d937ad4e9678a960 output/theme-catalog/sunset-coral.html +08d242bed264e0f31e1ab31ed79a37fb8a6bd18b91b63e79e05af175e97d4608 output/theme-catalog/theme-gallery.html +f39bb252bd52865c4551691a929d0cdf10d00eddd6a7f3ae0378f8bf2a8c5b55 output/theme-catalog/theme-gallery.pdf +23134c1e9992b5eb7c877b1c157d53ab0e36ac9c8ddcb2821b083f992b3dda39 prompts/anthropic-request-template.json +cd385809063a0badc73a37d7f87dc436886999785f8707c41bd57bd886fddc91 prompts/system-prompt.txt +0c6faf43172f0fa137d9c361dacceae4c5b972aef8623dcc14ea849bb1ccfb9b prompts/user-prompt-template.txt +c809c969ec7e11b9ff37784e9c6fcc0b7c5148d168e18b05aed797e03fbd6ead pytest.ini +6622f5ec0bc98804a17760ae13be6c9a9d1532eb36b69f72de01c749b565d454 renderer/Dockerfile +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 renderer/app/__init__.py +f46c52197e900e03b4a0b86d3b421534aa0387421c571ba743e3f0e041c2a0aa renderer/app/formatting.py +d515a0b7f4ea1976ba09903100409a3596f5f39e90aa496b4b7f2250a919d53f renderer/app/main.py +f18636cfa6ee4c5e509608cf047194eafb289b6798913595e4b43f3db7506178 renderer/app/renderer.py +8b7af092bdc0088832bd8f0e2fca15c10cb7c058ad4ad9d736ab7cbd677f97d6 renderer/app/schemas/claude-output-anthropic.schema.json +75b1bc32ae1e6d334279b385cf99712bf33cc78366e59c8f7013a39d11e334fc renderer/app/schemas/claude-output.schema.json +b1d9da86c2b0723f8c3bc3f03ffe89da15a099b1411b4e0a6c49a1e4be52b40a renderer/app/schemas/report-payload.schema.json +632d0dc09ca2bd9b98ac98aaf4540c14cd7c41c7cbad63358a56139303fce6e0 renderer/app/templates/_shared/footer.html.j2 +cff8e11e83cca48fe3a6fc59cf0d72be2349c3d7c1ad5b168cb1826d8975594c renderer/app/templates/_shared/macros.html.j2 +4c085ecc7a6418eb6d7fd673242dce76a65076b65c428baf6b1fff191354dd5a renderer/app/templates/classic/footer.html.j2 +140c3d2810b67d62729a57cc03aa45d651236f93d6e124c9caaca7267157a62b renderer/app/templates/classic/report.html.j2 +1690c913f1b92e45d40093338478ecff2b63e4a3efb92cf2e9e8da2af74c687e renderer/app/templates/dashboard/report.html.j2 +e43f73535b336a52da00f2e6c11699c4f304a819f0f738629cef65b23b6af8b0 renderer/app/templates/editorial/report.html.j2 +9f2827abc07b4888d2ffe9d5bb20062a242fcfcc0e50b183cb88bdd8532cbeb5 renderer/app/templates/ledger/report.html.j2 +b3acd510ba16424bb32baa2e08b1ac9de95d84ac912e39913e19348d02d81e13 renderer/app/templates/magazine/report.html.j2 +a89095e83da21a3c3f5db08d2abce769fcb6dbdb4f0bc89ae55e5aff0c740b86 renderer/app/templates/minimal/report.html.j2 +a63847d53e571ddb28fd5cf3593e0913e6273ad6c9a6d50c550f045e1fb38942 renderer/app/templates/modern/footer.html.j2 +cbb71a402abbd4ad4bcd95bfdfde6688a68d88c05168d6da51abbe72335e8446 renderer/app/templates/modern/report.html.j2 +dd1d096c8c34ac741d3d645ec2bc23c698c208376c61036bcaa2c9235dda7173 renderer/app/templates/poster/report.html.j2 +75a16ed98c86ba108e64d528c5292432c96101d4d466e27706c0ee4be216108e renderer/app/templates/sidebar/report.html.j2 +63c48859557e147be9ed8b929370d49de8d7c192d0d2ad0361075353e819b68a renderer/app/templates/split/report.html.j2 +30fbfcaf3973531384b8d0ed8df1b8868987a1be1d42d297953bf172db3d6e6d renderer/app/theme_catalog.py +1c8d516e13a21c16a306b35dedbc26929012f7afe03a8cb28899da09960b2d26 renderer/app/validation.py +35137cd8aeecd002116d489d98d0dc505909ec5d999d237387a094bd3e7f72f9 renderer/requirements.txt +e9ba2837656241375edd0f1027efe34c874d401efe87f3840a0a95e42a326ad0 scripts/render-local.py +0991e6bfdd450d1ce376b1cbf17170ba8c35342ac9bf320b30e858f0e45be49c scripts/render-theme-gallery.py +23b58558bad1c0c26a9095cb788f1b17bc8ba048c6db5fc9b24fa80b7097aa9b tests/conftest.py +811ecd465756c60292e65450434c6003ded577572315eee7f7809525ec253a49 tests/test_render.py +391a85063ce5ccafc11b23c24262292d05115c540d046a2dc177592233026852 tests/test_validation.py diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..239ff32 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,38 @@ +services: + report-renderer: + build: + context: ./renderer + container_name: monthly-report-renderer + restart: unless-stopped + environment: + TZ: ${TZ:-Europe/Berlin} + GOTENBERG_URL: ${GOTENBERG_URL:-http://gotenberg:3000} + RENDER_TIMEOUT_SECONDS: ${RENDER_TIMEOUT_SECONDS:-60} + MAX_PAYLOAD_BYTES: ${MAX_PAYLOAD_BYTES:-2097152} + ports: + - "${REPORT_RENDERER_PORT:-8080}:8080" + depends_on: + gotenberg: + condition: service_healthy + networks: + - report-net + + gotenberg: + image: gotenberg/gotenberg:8 + container_name: monthly-report-gotenberg + restart: unless-stopped + command: + - gotenberg + - --chromium-disable-javascript=true + - --api-timeout=90s + healthcheck: + test: ["CMD", "curl", "--fail", "http://localhost:3000/health"] + interval: 10s + timeout: 5s + retries: 10 + networks: + - report-net + +networks: + report-net: + name: monthly-report-net diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..5ee6477 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +testpaths = tests