cardreport 0.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. cardreport-0.2.1/.gitattributes +2 -0
  2. cardreport-0.2.1/.github/workflows/main.yml +142 -0
  3. cardreport-0.2.1/.gitignore +24 -0
  4. cardreport-0.2.1/.python-version +1 -0
  5. cardreport-0.2.1/AGENTS.md +30 -0
  6. cardreport-0.2.1/LICENSE +21 -0
  7. cardreport-0.2.1/PKG-INFO +92 -0
  8. cardreport-0.2.1/README.md +64 -0
  9. cardreport-0.2.1/docs/project/README.md +38 -0
  10. cardreport-0.2.1/docs/project/architecture.md +100 -0
  11. cardreport-0.2.1/docs/project/decisions/backend-selection.md +107 -0
  12. cardreport-0.2.1/docs/project/decisions/renderer-extension.md +59 -0
  13. cardreport-0.2.1/docs/project/decisions/report-templates.md +515 -0
  14. cardreport-0.2.1/docs/project/development.md +81 -0
  15. cardreport-0.2.1/docs/project/history/design.md +335 -0
  16. cardreport-0.2.1/docs/project/history/implementation-plan.md +344 -0
  17. cardreport-0.2.1/docs/project/history/implementation-status.md +95 -0
  18. cardreport-0.2.1/docs/project/history/p0-results.md +31 -0
  19. cardreport-0.2.1/docs/project/history/reference-report.md +25 -0
  20. cardreport-0.2.1/docs/project/history/reviews/2026-11-03-architecture-audit.md +80 -0
  21. cardreport-0.2.1/docs/project/history/reviews/2026-11-03-claude-backends.md +85 -0
  22. cardreport-0.2.1/docs/project/history/reviews/2026-11-03-claude-final.md +34 -0
  23. cardreport-0.2.1/docs/project/history/reviews/2026-11-03-claude-implementation.md +53 -0
  24. cardreport-0.2.1/docs/project/history/reviews/2026-11-03-claude-initial.md +79 -0
  25. cardreport-0.2.1/docs/project/history/reviews/2026-11-03-claude-p0.md +44 -0
  26. cardreport-0.2.1/docs/project/history/reviews/2026-11-03-claude-plan-final.md +45 -0
  27. cardreport-0.2.1/docs/project/history/reviews/2026-11-03-claude-plan-initial.md +30 -0
  28. cardreport-0.2.1/docs/project/history/reviews/2026-11-03-claude-working-final.md +27 -0
  29. cardreport-0.2.1/docs/project/history/reviews/2026-11-03-disposition.md +47 -0
  30. cardreport-0.2.1/docs/project/history/reviews/2026-11-03-implementation-disposition.md +54 -0
  31. cardreport-0.2.1/docs/project/history/reviews/2026-11-03-plan-disposition.md +51 -0
  32. cardreport-0.2.1/docs/project/history/reviews/2026-11-04-claude-refactor-final.md +25 -0
  33. cardreport-0.2.1/docs/project/history/reviews/2026-11-04-documentation.md +63 -0
  34. cardreport-0.2.1/docs/project/history/reviews/2026-11-04-release-automation.md +70 -0
  35. cardreport-0.2.1/docs/project/history/reviews/2026-11-04-renderer-refactor.md +68 -0
  36. cardreport-0.2.1/docs/project/history/reviews/2026-11-04-template-architecture.md +58 -0
  37. cardreport-0.2.1/docs/project/history/reviews/2026-11-04-template-implementation.md +78 -0
  38. cardreport-0.2.1/docs/project/releases.md +120 -0
  39. cardreport-0.2.1/docs/project/template-implementation-plan.md +223 -0
  40. cardreport-0.2.1/docs/user/api.md +528 -0
  41. cardreport-0.2.1/docs/user/command-line.md +52 -0
  42. cardreport-0.2.1/docs/user/datasets.md +151 -0
  43. cardreport-0.2.1/docs/user/examples.md +93 -0
  44. cardreport-0.2.1/docs/user/guide.md +113 -0
  45. cardreport-0.2.1/docs/user/quickstart.md +153 -0
  46. cardreport-0.2.1/docs/user/renderers.md +125 -0
  47. cardreport-0.2.1/docs/user/templates.md +99 -0
  48. cardreport-0.2.1/examples/flow_report.py +46 -0
  49. cardreport-0.2.1/examples/payments.csv +3 -0
  50. cardreport-0.2.1/examples/quickstart.py +29 -0
  51. cardreport-0.2.1/examples/reference_data.json +110 -0
  52. cardreport-0.2.1/examples/reference_data_second.json +110 -0
  53. cardreport-0.2.1/examples/reference_from_template.py +45 -0
  54. cardreport-0.2.1/examples/reference_report.py +131 -0
  55. cardreport-0.2.1/examples/reference_template.json +304 -0
  56. cardreport-0.2.1/pyproject.toml +80 -0
  57. cardreport-0.2.1/scripts/check_dist.py +159 -0
  58. cardreport-0.2.1/scripts/check_docs.py +129 -0
  59. cardreport-0.2.1/scripts/check_test_results.py +21 -0
  60. cardreport-0.2.1/scripts/release.py +289 -0
  61. cardreport-0.2.1/src/cardreport/__init__.py +94 -0
  62. cardreport-0.2.1/src/cardreport/_inputs.py +158 -0
  63. cardreport-0.2.1/src/cardreport/_pdf_checks.py +82 -0
  64. cardreport-0.2.1/src/cardreport/_svg_charts.py +129 -0
  65. cardreport-0.2.1/src/cardreport/_weasy_boxes.py +109 -0
  66. cardreport-0.2.1/src/cardreport/_weasy_worker.py +115 -0
  67. cardreport-0.2.1/src/cardreport/_worker.py +73 -0
  68. cardreport-0.2.1/src/cardreport/adapters/__init__.py +1 -0
  69. cardreport-0.2.1/src/cardreport/adapters/csv.py +84 -0
  70. cardreport-0.2.1/src/cardreport/adapters/pandas.py +69 -0
  71. cardreport-0.2.1/src/cardreport/assets/DejaVuSans-Bold.ttf +0 -0
  72. cardreport-0.2.1/src/cardreport/assets/DejaVuSans.ttf +0 -0
  73. cardreport-0.2.1/src/cardreport/assets/LICENSE_DEJAVU +99 -0
  74. cardreport-0.2.1/src/cardreport/assets/layout.js +106 -0
  75. cardreport-0.2.1/src/cardreport/assets/report.html +5 -0
  76. cardreport-0.2.1/src/cardreport/assets/style.css.j2 +18 -0
  77. cardreport-0.2.1/src/cardreport/charts.py +124 -0
  78. cardreport-0.2.1/src/cardreport/data.py +379 -0
  79. cardreport-0.2.1/src/cardreport/document.py +31 -0
  80. cardreport-0.2.1/src/cardreport/html.py +185 -0
  81. cardreport-0.2.1/src/cardreport/model.py +408 -0
  82. cardreport-0.2.1/src/cardreport/output.py +25 -0
  83. cardreport-0.2.1/src/cardreport/pdf.py +125 -0
  84. cardreport-0.2.1/src/cardreport/py.typed +0 -0
  85. cardreport-0.2.1/src/cardreport/renderers.py +43 -0
  86. cardreport-0.2.1/src/cardreport/templates.py +535 -0
  87. cardreport-0.2.1/src/cardreport/validation.py +311 -0
  88. cardreport-0.2.1/src/cardreport/weasyprint.py +112 -0
  89. cardreport-0.2.1/tests/test_adapters.py +176 -0
  90. cardreport-0.2.1/tests/test_data.py +239 -0
  91. cardreport-0.2.1/tests/test_model.py +125 -0
  92. cardreport-0.2.1/tests/test_release.py +175 -0
  93. cardreport-0.2.1/tests/test_renderer_contract.py +393 -0
  94. cardreport-0.2.1/tests/test_rendering.py +266 -0
  95. cardreport-0.2.1/tests/test_template_reference.py +114 -0
  96. cardreport-0.2.1/tests/test_templates.py +286 -0
  97. cardreport-0.2.1/tests/typing_cases.py +66 -0
  98. cardreport-0.2.1/uv.lock +1823 -0
@@ -0,0 +1,2 @@
1
+ # Preserve the bundled third-party license verbatim.
2
+ src/cardreport/assets/LICENSE_DEJAVU -whitespace
@@ -0,0 +1,142 @@
1
+ name: CI and release
2
+ on:
3
+ push:
4
+ branches: [main]
5
+ pull_request:
6
+ workflow_dispatch:
7
+ inputs:
8
+ pypi_tag:
9
+ description: 'Publish an existing verified GitHub Release to PyPI (vX.Y.Z)'
10
+ required: true
11
+ type: string
12
+ permissions:
13
+ contents: read
14
+ concurrency:
15
+ group: cardreport-${{ github.event_name == 'workflow_dispatch' && 'pypi-dispatch' || github.ref }}
16
+ cancel-in-progress: false
17
+ jobs:
18
+ setup-required:
19
+ if: github.event_name == 'workflow_dispatch' && (vars.PYPI_PUBLISH_ENABLED != 'true' || github.ref != 'refs/heads/main')
20
+ runs-on: ubuntu-24.04
21
+ steps:
22
+ - run: |
23
+ echo 'Select main, configure the PyPI Trusted Publisher and enable PYPI_PUBLISH_ENABLED first. See docs/project/releases.md.'
24
+ exit 1
25
+ verify:
26
+ if: github.event_name != 'workflow_dispatch'
27
+ runs-on: macos-15
28
+ timeout-minutes: 25
29
+ permissions:
30
+ contents: read
31
+ actions: read
32
+ outputs:
33
+ action: ${{ steps.plan.outputs.action }}
34
+ tag: ${{ steps.plan.outputs.tag }}
35
+ steps:
36
+ - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
37
+ with:
38
+ fetch-depth: 0
39
+ persist-credentials: false
40
+ - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
41
+ with:
42
+ python-version: '3.12'
43
+ - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
44
+ with:
45
+ version: '0.10.6'
46
+ - name: Install native PDF libraries and dependencies
47
+ run: |
48
+ brew install pango
49
+ echo "DYLD_FALLBACK_LIBRARY_PATH=$(brew --prefix)/lib" >> "$GITHUB_ENV"
50
+ uv sync --all-extras --locked
51
+ uv run --all-extras python -m playwright install chromium
52
+ - name: Lint and types
53
+ run: |
54
+ uv run --all-extras ruff check src tests examples scripts
55
+ uv run --all-extras ruff format --check src tests examples scripts
56
+ uv run --all-extras mypy
57
+ - name: Runtime and documentation
58
+ run: |
59
+ uv run --all-extras pytest -q --junitxml=local/ci-tests.xml
60
+ uv run --all-extras python scripts/check_test_results.py local/ci-tests.xml
61
+ uv run --all-extras python scripts/check_docs.py
62
+ - id: plan
63
+ name: Select release candidate
64
+ run: python scripts/release.py plan
65
+ - id: restore
66
+ name: Restore this run's verified distribution on retry
67
+ if: github.event_name == 'push' && github.repository == 'arch7tect/cardreport'
68
+ env:
69
+ GH_TOKEN: ${{ github.token }}
70
+ run: python scripts/release.py restore
71
+ - name: Build and freeze distribution
72
+ if: steps.restore.outputs.restored != 'true'
73
+ run: |
74
+ uv build --no-sources
75
+ rm -f dist/.gitignore
76
+ python scripts/release.py freeze
77
+ - name: Check packaged files and installed wheel
78
+ run: |
79
+ uvx --from twine==7.0.0 twine check --strict dist/*.whl dist/*.tar.gz
80
+ uv run --all-extras python scripts/check_dist.py
81
+ - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
82
+ if: steps.restore.outputs.restored != 'true'
83
+ with:
84
+ name: distribution
85
+ path: dist/
86
+ if-no-files-found: error
87
+ retention-days: 90
88
+ release:
89
+ needs: verify
90
+ if: github.event_name == 'push' && github.repository == 'arch7tect/cardreport' && needs.verify.outputs.action != 'skip'
91
+ runs-on: ubuntu-24.04
92
+ timeout-minutes: 10
93
+ permissions:
94
+ contents: write
95
+ steps:
96
+ - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
97
+ with:
98
+ fetch-depth: 0
99
+ - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
100
+ with:
101
+ python-version: '3.12'
102
+ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
103
+ with:
104
+ name: distribution
105
+ path: dist/
106
+ - name: Tag and publish the verified GitHub Release
107
+ env:
108
+ GH_TOKEN: ${{ github.token }}
109
+ run: python scripts/release.py github
110
+ - name: Report PyPI setup status
111
+ if: vars.PYPI_PUBLISH_ENABLED != 'true'
112
+ run: echo 'PyPI upload is disabled until the Trusted Publisher is configured. See docs/project/releases.md.' >> "$GITHUB_STEP_SUMMARY"
113
+ pypi:
114
+ needs: [verify, release]
115
+ if: >-
116
+ always() && !cancelled() && github.repository == 'arch7tect/cardreport' &&
117
+ github.ref == 'refs/heads/main' && vars.PYPI_PUBLISH_ENABLED == 'true' &&
118
+ (needs.release.result == 'success' || github.event_name == 'workflow_dispatch')
119
+ runs-on: ubuntu-24.04
120
+ timeout-minutes: 10
121
+ environment: pypi
122
+ permissions:
123
+ contents: read
124
+ id-token: write
125
+ steps:
126
+ - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
127
+ with:
128
+ fetch-depth: 0
129
+ persist-credentials: false
130
+ - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
131
+ with:
132
+ python-version: '3.12'
133
+ - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
134
+ with:
135
+ version: '0.10.6'
136
+ - name: Download and verify the canonical release assets
137
+ env:
138
+ GH_TOKEN: ${{ github.token }}
139
+ RELEASE_TAG: ${{ inputs.pypi_tag || needs.verify.outputs.tag }}
140
+ run: python scripts/release.py download --tag "$RELEASE_TAG"
141
+ - name: Publish the same bytes using Trusted Publishing
142
+ run: uv publish --trusted-publishing always --check-url https://pypi.org/simple/ dist/*.whl dist/*.tar.gz
@@ -0,0 +1,24 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+ # Local IDE, configuration, and generated reports
13
+ .idea/
14
+ .env
15
+ .env.*
16
+ !.env.template
17
+ output/
18
+ local/
19
+ .DS_Store
20
+
21
+ # Local check caches
22
+ .mypy_cache/
23
+ .pytest_cache/
24
+ .ruff_cache/
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,30 @@
1
+ # CardReport contributor instructions
2
+
3
+ Use Python 3.12+ and uv. Write source, comments and documentation in English.
4
+ Read docs/project/architecture.md and the relevant decision before changing the
5
+ public API. Keep README.md and docs/user/api.md aligned with implemented behavior.
6
+ README is the user entry point; tutorials/reference live in docs/user. Project
7
+ rationale lives in docs/project; dated history is not the current API contract.
8
+
9
+ Chromium is the production default. The provisional Renderer protocol also supports
10
+ an experimental SVG/WeasyPrint subset. See docs/user/renderers.md for capabilities
11
+ and docs/project/decisions/renderer-extension.md for rationale. Keep renderers behind
12
+ typed public components. No arbitrary Plotly kwargs, user HTML/CSS or template
13
+ plugins. Prefer small concrete modules to a framework; no BI server, query layer,
14
+ telemetry or network-dependent report assets.
15
+
16
+ Use uv sync --all-extras --locked for development and uv build for packaging.
17
+ Follow docs/project/development.md for checks; validate snippets and links with
18
+ uv run --all-extras python scripts/check_docs.py when changing documentation.
19
+ PDF implementation must include selected-engine integration checks, output
20
+ inspection, font readiness, hostile-label coverage and overflow rejection. Browser
21
+ lifecycle checks apply only if the browser candidate is selected. Never silently
22
+ shrink, truncate or rasterize unsupported content. Add meaningful tests with
23
+ implementation, not tests for the existence of scaffold files.
24
+
25
+ Keep .venv, .idea, local config and generated reports ignored. Do not overwrite
26
+ existing reports by default. A user-authorized version bump pushed to main requests
27
+ release through .github/workflows/main.yml. That workflow may create the matching
28
+ tag/GitHub Release and publish the same verified assets to PyPI when its setup gate
29
+ is enabled. Never move released tags or replace released artifacts. Repository
30
+ creation and unrelated publication still require a request. See docs/project/releases.md.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Oleg Orlov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,92 @@
1
+ Metadata-Version: 2.5
2
+ Name: cardreport
3
+ Version: 0.2.1
4
+ Summary: Composable Python reports with cards, constrained charts, and HTML-to-PDF output
5
+ Project-URL: Repository, https://github.com/arch7tect/cardreport
6
+ Project-URL: Documentation, https://github.com/arch7tect/cardreport/blob/main/docs/user/quickstart.md
7
+ Project-URL: Issues, https://github.com/arch7tect/cardreport/issues
8
+ Author: Oleg Orlov
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Requires-Python: >=3.12
12
+ Requires-Dist: fonttools<5,>=4.59
13
+ Requires-Dist: jinja2<4,>=3.1
14
+ Requires-Dist: plotly<7,>=6.9
15
+ Provides-Extra: pandas
16
+ Requires-Dist: pandas<4,>=2.2; extra == 'pandas'
17
+ Provides-Extra: pdf
18
+ Requires-Dist: playwright==1.64.1; extra == 'pdf'
19
+ Requires-Dist: psutil<8,>=7; extra == 'pdf'
20
+ Requires-Dist: pypdf<7,>=6; extra == 'pdf'
21
+ Provides-Extra: weasyprint
22
+ Requires-Dist: matplotlib==3.11.2; extra == 'weasyprint'
23
+ Requires-Dist: pdfplumber<1,>=0.11.10; extra == 'weasyprint'
24
+ Requires-Dist: psutil<8,>=7; extra == 'weasyprint'
25
+ Requires-Dist: pypdf<7,>=6; extra == 'weasyprint'
26
+ Requires-Dist: weasyprint==70.0; extra == 'weasyprint'
27
+ Description-Content-Type: text/markdown
28
+
29
+ # CardReport
30
+
31
+ [![Latest release](https://img.shields.io/github/v/release/arch7tect/cardreport)](https://github.com/arch7tect/cardreport/releases/latest)
32
+ [![PyPI version](https://img.shields.io/pypi/v/cardreport)](https://pypi.org/project/cardreport/)
33
+
34
+ Build static reports in Python from cards, KPIs, charts and tables. Compose a
35
+ report, then get standalone HTML or PDF bytes for your application. PDF text is
36
+ selectable and charts are vector graphics.
37
+
38
+ ## Install in your project
39
+
40
+ Python 3.12+ is required. Install from PyPI with uv, then install the PDF browser runtime:
41
+
42
+ ```sh
43
+ uv add "cardreport[pdf]"
44
+ uv run python -m playwright install chromium
45
+ ```
46
+
47
+ Keep your application’s uv.lock to pin the installed version. For HTML-only use, omit `[pdf]` and the
48
+ browser-install command. See [installation and quickstart](https://github.com/arch7tect/cardreport/blob/main/docs/user/quickstart.md)
49
+ for local checkouts, wheels and how to embed exports.
50
+
51
+ ## Use from Python
52
+
53
+ ```python
54
+ from cardreport import BarChart, Card, KPI, Report
55
+
56
+ report = Report("Call results")
57
+ report.add(Card(KPI("Total calls", 34)))
58
+ report.row(
59
+ Card(BarChart(labels=["Answered", "No answer"], values=[24, 10])),
60
+ height_mm=70,
61
+ )
62
+
63
+ html: str = report.to_html()
64
+ pdf: bytes = report.to_pdf()
65
+ ```
66
+
67
+ Return `pdf` from your application, put it in storage, or use
68
+ `report.write_pdf("report.pdf")` to save a file. Existing files are preserved by
69
+ default. Reports reject content that does not fit rather than silently shrinking it.
70
+
71
+ ## Documentation
72
+
73
+ - [Quickstart](https://github.com/arch7tect/cardreport/blob/main/docs/user/quickstart.md) — build from application data; return strings/bytes.
74
+ - [Templates](https://github.com/arch7tect/cardreport/blob/main/docs/user/templates.md) — JSON presentation with separate named datasets.
75
+ - [Datasets](https://github.com/arch7tect/cardreport/blob/main/docs/user/datasets.md) — native Python data and typed JSON storage.
76
+ - [User guide](https://github.com/arch7tect/cardreport/blob/main/docs/user/guide.md) — cards, layout, formatting and multiple pages.
77
+ - [API reference](https://github.com/arch7tect/cardreport/blob/main/docs/user/api.md) — signatures, defaults, constraints and errors.
78
+ - [Renderers](https://github.com/arch7tect/cardreport/blob/main/docs/user/renderers.md) — installation, supported features and extensions.
79
+ - [Examples and integration](https://github.com/arch7tect/cardreport/blob/main/docs/user/examples.md) — the original report, tables and async code.
80
+ - [Command-line examples](https://github.com/arch7tect/cardreport/blob/main/docs/user/command-line.md) — optional script runners.
81
+
82
+ Chromium is the default renderer. The experimental SVG/WeasyPrint renderer works
83
+ without JavaScript or a browser, with a smaller supported feature set. Both can
84
+ render the [original reference example](https://github.com/arch7tect/cardreport/blob/main/docs/user/examples.md#reference-report).
85
+ Local macOS/Python 3.12 is verified; Linux and Windows are not yet verified.
86
+
87
+ For architecture, decisions and contributing, see [project documentation](https://github.com/arch7tect/cardreport/blob/main/docs/project/README.md).
88
+
89
+ ## License
90
+
91
+ [MIT](https://github.com/arch7tect/cardreport/blob/main/LICENSE). Bundled DejaVu fonts retain their [font license](https://github.com/arch7tect/cardreport/blob/main/src/cardreport/assets/LICENSE_DEJAVU).
92
+ Third-party rendering packages retain their own licenses.
@@ -0,0 +1,64 @@
1
+ # CardReport
2
+
3
+ [![Latest release](https://img.shields.io/github/v/release/arch7tect/cardreport)](https://github.com/arch7tect/cardreport/releases/latest)
4
+ [![PyPI version](https://img.shields.io/pypi/v/cardreport)](https://pypi.org/project/cardreport/)
5
+
6
+ Build static reports in Python from cards, KPIs, charts and tables. Compose a
7
+ report, then get standalone HTML or PDF bytes for your application. PDF text is
8
+ selectable and charts are vector graphics.
9
+
10
+ ## Install in your project
11
+
12
+ Python 3.12+ is required. Install from PyPI with uv, then install the PDF browser runtime:
13
+
14
+ ```sh
15
+ uv add "cardreport[pdf]"
16
+ uv run python -m playwright install chromium
17
+ ```
18
+
19
+ Keep your application’s uv.lock to pin the installed version. For HTML-only use, omit `[pdf]` and the
20
+ browser-install command. See [installation and quickstart](https://github.com/arch7tect/cardreport/blob/main/docs/user/quickstart.md)
21
+ for local checkouts, wheels and how to embed exports.
22
+
23
+ ## Use from Python
24
+
25
+ ```python
26
+ from cardreport import BarChart, Card, KPI, Report
27
+
28
+ report = Report("Call results")
29
+ report.add(Card(KPI("Total calls", 34)))
30
+ report.row(
31
+ Card(BarChart(labels=["Answered", "No answer"], values=[24, 10])),
32
+ height_mm=70,
33
+ )
34
+
35
+ html: str = report.to_html()
36
+ pdf: bytes = report.to_pdf()
37
+ ```
38
+
39
+ Return `pdf` from your application, put it in storage, or use
40
+ `report.write_pdf("report.pdf")` to save a file. Existing files are preserved by
41
+ default. Reports reject content that does not fit rather than silently shrinking it.
42
+
43
+ ## Documentation
44
+
45
+ - [Quickstart](https://github.com/arch7tect/cardreport/blob/main/docs/user/quickstart.md) — build from application data; return strings/bytes.
46
+ - [Templates](https://github.com/arch7tect/cardreport/blob/main/docs/user/templates.md) — JSON presentation with separate named datasets.
47
+ - [Datasets](https://github.com/arch7tect/cardreport/blob/main/docs/user/datasets.md) — native Python data and typed JSON storage.
48
+ - [User guide](https://github.com/arch7tect/cardreport/blob/main/docs/user/guide.md) — cards, layout, formatting and multiple pages.
49
+ - [API reference](https://github.com/arch7tect/cardreport/blob/main/docs/user/api.md) — signatures, defaults, constraints and errors.
50
+ - [Renderers](https://github.com/arch7tect/cardreport/blob/main/docs/user/renderers.md) — installation, supported features and extensions.
51
+ - [Examples and integration](https://github.com/arch7tect/cardreport/blob/main/docs/user/examples.md) — the original report, tables and async code.
52
+ - [Command-line examples](https://github.com/arch7tect/cardreport/blob/main/docs/user/command-line.md) — optional script runners.
53
+
54
+ Chromium is the default renderer. The experimental SVG/WeasyPrint renderer works
55
+ without JavaScript or a browser, with a smaller supported feature set. Both can
56
+ render the [original reference example](https://github.com/arch7tect/cardreport/blob/main/docs/user/examples.md#reference-report).
57
+ Local macOS/Python 3.12 is verified; Linux and Windows are not yet verified.
58
+
59
+ For architecture, decisions and contributing, see [project documentation](https://github.com/arch7tect/cardreport/blob/main/docs/project/README.md).
60
+
61
+ ## License
62
+
63
+ [MIT](https://github.com/arch7tect/cardreport/blob/main/LICENSE). Bundled DejaVu fonts retain their [font license](https://github.com/arch7tect/cardreport/blob/main/src/cardreport/assets/LICENSE_DEJAVU).
64
+ Third-party rendering packages retain their own licenses.
@@ -0,0 +1,38 @@
1
+ # Project documentation
2
+
3
+ [User documentation](../../README.md)
4
+
5
+ These documents explain implementation and maintenance. Application developers
6
+ should begin with the [quickstart](../user/quickstart.md) and [API reference](../user/api.md).
7
+
8
+ ## Current implementation
9
+
10
+ - [Release operations](releases.md) — version bumps, GitHub/PyPI setup and recovery.
11
+
12
+ - [Architecture](architecture.md) — module boundaries, snapshots and rendering.
13
+ - [Development](development.md) — setup, checks, packaging and documentation maintenance.
14
+ - [Renderer extension decision](decisions/renderer-extension.md) — current extension scope.
15
+
16
+ ## Decisions and history
17
+
18
+ - [Release automation review](history/reviews/2026-11-04-release-automation.md) — implementation findings and verification.
19
+
20
+ - [Template implementation review](history/reviews/2026-11-04-template-implementation.md) — delivery, advisory findings and release checks.
21
+
22
+ - [Template implementation plan](template-implementation-plan.md) — six delivery slices with documentation and acceptance checks.
23
+ - [JSON templates and named datasets](decisions/report-templates.md) — implemented extension above the Python API.
24
+ - [Template architecture review](history/reviews/2026-11-04-template-architecture.md) — findings, scope decisions and verification.
25
+ - [Initial backend selection](decisions/backend-selection.md) — historical Chromium selection.
26
+ - [Original design](history/design.md) — reviewed baseline, superseded where current docs differ.
27
+ - [Implementation plan](history/implementation-plan.md) — completed initial stages.
28
+ - [Implementation status](history/implementation-status.md) — dated delivery record.
29
+ - [P0 comparison](history/p0-results.md) — original renderer experiment.
30
+ - [Reference inventory](history/reference-report.md) — source composition and provenance.
31
+ - [Architecture audit](history/reviews/2026-11-03-architecture-audit.md).
32
+ - [Renderer refactor and verification](history/reviews/2026-11-04-renderer-refactor.md).
33
+ - [Final renderer advisory review](history/reviews/2026-11-04-claude-refactor-final.md).
34
+ - [Documentation review and verification](history/reviews/2026-11-04-documentation.md).
35
+
36
+ Other dated [review records](history/reviews) preserve the earlier discussions.
37
+ History documents record what was decided/tested at that time; they are not the
38
+ current user API specification. No report evidence or original source PDF is published.
@@ -0,0 +1,100 @@
1
+ # Architecture
2
+
3
+ [Project documentation](README.md) · [Public API](../user/api.md)
4
+
5
+ CardReport is a Python library. Applications own data access and business metrics.
6
+ The library owns typed composition, semantic validation, rendering and output
7
+ publication. It has no service, query layer, plugin registry or custom geometry IR.
8
+
9
+ ## Export path
10
+
11
+ ```text
12
+ Optional ReportTemplate + named Dataset snapshots
13
+ -> bind -> Report + frozen components
14
+ -> semantic validation and chart normalization
15
+ -> frozen ReportDocument
16
+ -> Renderer (HTML preview or checked PDF)
17
+ -> optional atomic file publication
18
+ ```
19
+
20
+ `Report` is a mutable builder. Components copy their input sequences and table rows.
21
+ Validation checks structure, values, formats, glyph coverage and bounds on input
22
+ size. It creates a snapshot retaining the public component types and a read-only
23
+ mapping of normalized chart data. Chart coordinates are floats; value labels are
24
+ formatted from original values. Strings remain unescaped until output generation.
25
+ A snapshot contains no page coordinates. PDF deadlines start before validation.
26
+
27
+ ## Modules
28
+
29
+ | Module | Responsibility |
30
+ | --- | --- |
31
+ | [data.py](../../src/cardreport/data.py) | Immutable tables, schemas, bounded native and tagged JSON input |
32
+ | [templates.py](../../src/cardreport/templates.py) | Closed JSON grammar and binding to existing components |
33
+ | [_inputs.py](../../src/cardreport/_inputs.py) | Shared bounded parsing and located errors |
34
+ | [adapters](../../src/cardreport/adapters/) | Single CSV file and lazy optional pandas ingestion |
35
+ | [model.py](../../src/cardreport/model.py) | Components, aliases, formats, errors and Report facade |
36
+ | [validation.py](../../src/cardreport/validation.py) | Independent semantic checks and normalization |
37
+ | [document.py](../../src/cardreport/document.py) | Frozen export and chart records |
38
+ | [renderers.py](../../src/cardreport/renderers.py) | Provisional Renderer Protocol and Chromium entry point |
39
+ | [html.py](../../src/cardreport/html.py) | Escaped shared fragments and Chromium HTML document |
40
+ | [charts.py](../../src/cardreport/charts.py) | Private Plotly specification adapter |
41
+ | [pdf.py](../../src/cardreport/pdf.py) | Owned worker, deadline, error transport and cleanup |
42
+ | [output.py](../../src/cardreport/output.py) | Atomic publication and no-clobber policy |
43
+ | [weasyprint.py](../../src/cardreport/weasyprint.py) | Experimental renderer and capability checks |
44
+
45
+ ## Chromium
46
+
47
+ The HTML embeds fonts, Plotly (when needed), settings and the
48
+ [layout script](../../src/cardreport/assets/layout.js). The browser loads fonts,
49
+ places blocks on fixed pages, fragments top-level flow tables, draws charts and
50
+ checks page/component/chart-label bounds. Plotly DOM inspection stays within this
51
+ engine. `window.cardreport` signals ready/pages or a typed error; the
52
+ [worker](../../src/cardreport/_worker.py) waits, prints and checks page count.
53
+ Network requests are blocked. PDF export runs in an owned subprocess to avoid
54
+ caller event-loop conflicts and to stop the process tree on timeout/interruption.
55
+
56
+ ## Experimental WeasyPrint
57
+
58
+ Capabilities are defined in the [user-facing table](../user/renderers.md#capabilities).
59
+ The engine reuses validated components, shared markup, themes and publication.
60
+ It owns native layout and checks; it does not reuse Chromium pagination code.
61
+
62
+ The [box adapter](../../src/cardreport/_weasy_boxes.py) isolates private WeasyPrint
63
+ 70.0 geometry. Positive/negative bundled-font canaries fail closed on incompatibility.
64
+ Two document layout passes measure placeholders and verify final SVG placement.
65
+ [Matplotlib SVG bars](../../src/cardreport/_svg_charts.py) use isolated default settings
66
+ and the bundled font. A separate native SVG preflight checks actual PDF glyph bounds
67
+ with pdfplumber, because Matplotlib and native text metrics can differ.
68
+ [PDF checks](../../src/cardreport/_pdf_checks.py) recurse through form resources and
69
+ compare embedded font identity/version with bundled fonts.
70
+
71
+ The [worker](../../src/cardreport/_weasy_worker.py) allows only exact bundled font
72
+ URLs and rejects any other attempted fetch. Its version-specific fetcher flag is
73
+ also tied to the WeasyPrint pin. Native system-library setup belongs to the caller.
74
+ Unsupported features fail explicitly rather than switching engines or weakening checks.
75
+
76
+ ## Maintenance boundaries
77
+
78
+ Keep the component vocabulary closed and strongly typed. A new component requires
79
+ validation, rendering support or explicit rejection, API documentation and tests.
80
+ New renderer implementations satisfy the shared semantic/error/output contract,
81
+ while owning their geometry and lifecycle. Do not add a registry or inheritance
82
+ framework merely to reuse helpers. See the [extension decision](decisions/renderer-extension.md)
83
+ for the rationale and the [development guide](development.md) for verification.
84
+
85
+ ## Separate presentation and data
86
+
87
+ Version 0.2.0 adds a layer above Report, with independent `cardreport.template/1`
88
+ and `cardreport.data/1` envelopes. Templates snapshot a bounded validated definition;
89
+ Dataset copies native rows and fixed-offset datetimes. Schemas distinguish missing,
90
+ null and empty input. Binding checks every declared contract, constructs ordinary
91
+ components and calls existing validation; no unresolved expressions reach renderers.
92
+ Semantic errors map known component paths back to template paths, preserving causes.
93
+ Static validation uses one representative row per declared field type plus explicit
94
+ binding/type checks. Real row counts, values and geometry are checked later.
95
+
96
+ `from_csv` requires a schema and uses the standard library. `from_pandas` imports its
97
+ optional dependency only on invocation. Neither source access nor normalization is
98
+ part of bind/render. No source registry, SQL execution, directory scanner, expressions
99
+ or model-to-template serializer is introduced. See the [decision](decisions/report-templates.md)
100
+ and [delivery plan](template-implementation-plan.md).
@@ -0,0 +1,107 @@
1
+ # Rendering backend decision gate
2
+
3
+ > Update 2026-11-03: the owner authorized typed components, independent validation
4
+ > and a provisional renderer extension with an experimental SVG/WeasyPrint path.
5
+ > See [renderer extension](renderer-extension.md); the original decision below is historical.
6
+
7
+
8
+ Date: 2026-11-03. Status: A selected for v0.1 after a local comparative experiment and Claude review.
9
+ Owner: Oleg Orlov. Current result: [P0 findings](../history/p0-results.md) and
10
+ [Claude checkpoint](../history/reviews/2026-11-03-claude-p0.md). The user subsequently authorized
11
+ autonomous implementation and routine internal choices. A provides direct measured
12
+ overflow checks without a second layout engine. Final implementation verification
13
+ is tracked in [implementation status](../history/implementation-status.md).
14
+
15
+ The sections below preserve the original decision criteria and candidate discussion;
16
+ future-tense experiment/install statements describe the pre-implementation state.
17
+ This record supersedes any unconditional choice of Chromium
18
+ in the initial draft. There will be one production renderer, not a plugin system.
19
+
20
+ ## Requirement and review outcome
21
+
22
+ The user wants a small CardReport API with cards, KPIs, constrained charts, rows
23
+ and columns. Interactivity is unnecessary. The user explicitly asked to keep a
24
+ JavaScript-free and browser-free option open and consult Claude.
25
+
26
+ [Claude's comparison](../history/reviews/2026-11-03-claude-backends.md) recommends testing
27
+ static Python-generated SVG + Jinja2 + WeasyPrint first, with Plotly + Jinja2 +
28
+ Chromium as fallback. This is advice based on document review, not tested evidence.
29
+ The maintainer has not selected a final renderer. The external API remains the
30
+ same in both candidates.
31
+
32
+ ## Candidates
33
+
34
+ | Concern | A: Plotly + Chromium | B: static SVG + WeasyPrint |
35
+ | --- | --- | --- |
36
+ | Charts | Mature private chart adapter; disable interaction and force SVG traces | Matplotlib SVG or a deliberately small direct-SVG subset; no Plotly |
37
+ | Installation | Playwright plus explicitly installed Chromium/system libraries | WeasyPrint with Pango and related native libraries; Matplotlib adds dependencies |
38
+ | Report layout | Browser is sole measurement/layout authority | WeasyPrint is final PDF authority; usable geometry/overflow strategy must be proved |
39
+ | Preview | Standalone HTML needs JavaScript; test against the pinned Chromium | Static HTML has no JavaScript, but browser and PDF layout may differ |
40
+ | Hard part | Readiness, label sizing, browser lifecycle and PDF inspection | Text metrics, CSS support, overflow checks and possibly chart/line-breaking code |
41
+
42
+ The [Plotly documentation](https://plotly.com/python/static-image-export/)
43
+ confirms that current Kaleido static export needs Chrome; it is not candidate B.
44
+ [WeasyPrint installation](https://doc.courtbouillon.org/weasyprint/stable/first_steps.html)
45
+ requires Pango, so browser-free does not mean system-dependency-free.
46
+ [WeasyPrint API/versioning](https://doc.courtbouillon.org/weasyprint/stable/api_reference.html)
47
+ warns that rendering may change across major releases and private APIs are not stable.
48
+ [Matplotlib fonts](https://matplotlib.org/stable/users/explain/text/fonts.html)
49
+ describes SVG text output options. Preserving text nodes alone does not prove
50
+ selectable text or font fidelity after conversion to PDF.
51
+
52
+ ## Disposition of Claude's recommendations
53
+
54
+ Accepted: keep B open; no automatic backend abstraction; evaluate selectable text,
55
+ fonts, clipping, native installation and table splitting on actual output; keep
56
+ browser-specific implementation conditional. No backend implementation is included
57
+ in the project-creation task.
58
+
59
+ Qualified: Claude favors direct SVG and Python-owned geometry/line breaking. This
60
+ would create chart axes, legends, wrapping and pagination code. It is not accepted
61
+ as an architectural decision without evidence that it is simpler to maintain than
62
+ Matplotlib or the existing browser engine. A bundled font reduces metric drift but
63
+ does not prove identical layout across FreeType, Pango and a preview browser.
64
+
65
+ Rejected as a pass condition: dependence on undocumented WeasyPrint box-tree APIs
66
+ must not silently count as success. Any need for private APIs or a second text
67
+ layout engine is a recorded decision tradeoff, not a default implementation detail.
68
+ Resource loading must allow exact bundled font/image resources via a controlled
69
+ resolver; a blanket failing URL fetcher would also break those embedded assets.
70
+ No network or arbitrary filesystem loading is allowed.
71
+
72
+ Claude's sample reused the old 90 mm / [3,1] geometry. Use the corrected 100 mm /
73
+ [2,1] fixture from design.md so the KPI share exceeds its 28 mm minimum.
74
+
75
+ ## Small staged experiment (future implementation work)
76
+
77
+ 1. One shared fixture: A4 landscape, 3 KPI cards, weighted nested row/column,
78
+ funnel, grouped bar, date line with a gap; long English/Cyrillic labels and
79
+ literal markup; a deliberate overflow variant. Use synthetic data only.
80
+ 2. Produce A and B versions outside src, with no common backend framework.
81
+ First test Matplotlib SVG with text retained; test direct SVG only for the
82
+ representative bar/funnel if that could materially simplify dependency/code cost.
83
+ 3. Check selectable expected labels and numbers, no raster images, no clipping,
84
+ pinned-font use, visual alignment, explicit overflow failure and zero external
85
+ fetches. Compare measured text/box positions with an initial 0.5 mm tolerance.
86
+ 4. If B passes dashboard checks, test a separate 120-row flow report with repeated
87
+ headings, exactly-once row IDs and oversized-row rejection. Flow remains Slice 2,
88
+ but this probe must reveal whether the later requirement is achievable.
89
+ 5. Record installation steps, dependency footprint, code needed and failures on
90
+ macOS and Linux. Windows support remains unspecified; confirm before claiming
91
+ cross-platform readiness. Do not install new OS packages silently as part of
92
+ this design-only task.
93
+
94
+ Select B only if vector/selectable text, layout, reliable overflow detection and
95
+ acceptable installation pass without disproportionate chart/layout code. Otherwise
96
+ retain A and record the concrete failing gate. If neither passes, revise the
97
+ requirements or design explicitly; do not weaken no-clipping guarantees silently.
98
+ For B, the PDF is authoritative and browser preview is approximate unless fixtures
99
+ prove parity for the supported subset. Resolve this contract before implementation.
100
+
101
+ ## Dependency status
102
+
103
+ The uv scaffold initially resolved Jinja2, Plotly and Playwright for candidate A.
104
+ These are not a final runtime commitment. Candidate-A packages are now in the
105
+ optional browser extra. B dependencies will be added only for the comparative
106
+ spike or after selection; WeasyPrint/Matplotlib are not tested or installed by
107
+ this task. Chromium is not downloaded by this task.