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.
- cardreport-0.2.1/.gitattributes +2 -0
- cardreport-0.2.1/.github/workflows/main.yml +142 -0
- cardreport-0.2.1/.gitignore +24 -0
- cardreport-0.2.1/.python-version +1 -0
- cardreport-0.2.1/AGENTS.md +30 -0
- cardreport-0.2.1/LICENSE +21 -0
- cardreport-0.2.1/PKG-INFO +92 -0
- cardreport-0.2.1/README.md +64 -0
- cardreport-0.2.1/docs/project/README.md +38 -0
- cardreport-0.2.1/docs/project/architecture.md +100 -0
- cardreport-0.2.1/docs/project/decisions/backend-selection.md +107 -0
- cardreport-0.2.1/docs/project/decisions/renderer-extension.md +59 -0
- cardreport-0.2.1/docs/project/decisions/report-templates.md +515 -0
- cardreport-0.2.1/docs/project/development.md +81 -0
- cardreport-0.2.1/docs/project/history/design.md +335 -0
- cardreport-0.2.1/docs/project/history/implementation-plan.md +344 -0
- cardreport-0.2.1/docs/project/history/implementation-status.md +95 -0
- cardreport-0.2.1/docs/project/history/p0-results.md +31 -0
- cardreport-0.2.1/docs/project/history/reference-report.md +25 -0
- cardreport-0.2.1/docs/project/history/reviews/2026-11-03-architecture-audit.md +80 -0
- cardreport-0.2.1/docs/project/history/reviews/2026-11-03-claude-backends.md +85 -0
- cardreport-0.2.1/docs/project/history/reviews/2026-11-03-claude-final.md +34 -0
- cardreport-0.2.1/docs/project/history/reviews/2026-11-03-claude-implementation.md +53 -0
- cardreport-0.2.1/docs/project/history/reviews/2026-11-03-claude-initial.md +79 -0
- cardreport-0.2.1/docs/project/history/reviews/2026-11-03-claude-p0.md +44 -0
- cardreport-0.2.1/docs/project/history/reviews/2026-11-03-claude-plan-final.md +45 -0
- cardreport-0.2.1/docs/project/history/reviews/2026-11-03-claude-plan-initial.md +30 -0
- cardreport-0.2.1/docs/project/history/reviews/2026-11-03-claude-working-final.md +27 -0
- cardreport-0.2.1/docs/project/history/reviews/2026-11-03-disposition.md +47 -0
- cardreport-0.2.1/docs/project/history/reviews/2026-11-03-implementation-disposition.md +54 -0
- cardreport-0.2.1/docs/project/history/reviews/2026-11-03-plan-disposition.md +51 -0
- cardreport-0.2.1/docs/project/history/reviews/2026-11-04-claude-refactor-final.md +25 -0
- cardreport-0.2.1/docs/project/history/reviews/2026-11-04-documentation.md +63 -0
- cardreport-0.2.1/docs/project/history/reviews/2026-11-04-release-automation.md +70 -0
- cardreport-0.2.1/docs/project/history/reviews/2026-11-04-renderer-refactor.md +68 -0
- cardreport-0.2.1/docs/project/history/reviews/2026-11-04-template-architecture.md +58 -0
- cardreport-0.2.1/docs/project/history/reviews/2026-11-04-template-implementation.md +78 -0
- cardreport-0.2.1/docs/project/releases.md +120 -0
- cardreport-0.2.1/docs/project/template-implementation-plan.md +223 -0
- cardreport-0.2.1/docs/user/api.md +528 -0
- cardreport-0.2.1/docs/user/command-line.md +52 -0
- cardreport-0.2.1/docs/user/datasets.md +151 -0
- cardreport-0.2.1/docs/user/examples.md +93 -0
- cardreport-0.2.1/docs/user/guide.md +113 -0
- cardreport-0.2.1/docs/user/quickstart.md +153 -0
- cardreport-0.2.1/docs/user/renderers.md +125 -0
- cardreport-0.2.1/docs/user/templates.md +99 -0
- cardreport-0.2.1/examples/flow_report.py +46 -0
- cardreport-0.2.1/examples/payments.csv +3 -0
- cardreport-0.2.1/examples/quickstart.py +29 -0
- cardreport-0.2.1/examples/reference_data.json +110 -0
- cardreport-0.2.1/examples/reference_data_second.json +110 -0
- cardreport-0.2.1/examples/reference_from_template.py +45 -0
- cardreport-0.2.1/examples/reference_report.py +131 -0
- cardreport-0.2.1/examples/reference_template.json +304 -0
- cardreport-0.2.1/pyproject.toml +80 -0
- cardreport-0.2.1/scripts/check_dist.py +159 -0
- cardreport-0.2.1/scripts/check_docs.py +129 -0
- cardreport-0.2.1/scripts/check_test_results.py +21 -0
- cardreport-0.2.1/scripts/release.py +289 -0
- cardreport-0.2.1/src/cardreport/__init__.py +94 -0
- cardreport-0.2.1/src/cardreport/_inputs.py +158 -0
- cardreport-0.2.1/src/cardreport/_pdf_checks.py +82 -0
- cardreport-0.2.1/src/cardreport/_svg_charts.py +129 -0
- cardreport-0.2.1/src/cardreport/_weasy_boxes.py +109 -0
- cardreport-0.2.1/src/cardreport/_weasy_worker.py +115 -0
- cardreport-0.2.1/src/cardreport/_worker.py +73 -0
- cardreport-0.2.1/src/cardreport/adapters/__init__.py +1 -0
- cardreport-0.2.1/src/cardreport/adapters/csv.py +84 -0
- cardreport-0.2.1/src/cardreport/adapters/pandas.py +69 -0
- cardreport-0.2.1/src/cardreport/assets/DejaVuSans-Bold.ttf +0 -0
- cardreport-0.2.1/src/cardreport/assets/DejaVuSans.ttf +0 -0
- cardreport-0.2.1/src/cardreport/assets/LICENSE_DEJAVU +99 -0
- cardreport-0.2.1/src/cardreport/assets/layout.js +106 -0
- cardreport-0.2.1/src/cardreport/assets/report.html +5 -0
- cardreport-0.2.1/src/cardreport/assets/style.css.j2 +18 -0
- cardreport-0.2.1/src/cardreport/charts.py +124 -0
- cardreport-0.2.1/src/cardreport/data.py +379 -0
- cardreport-0.2.1/src/cardreport/document.py +31 -0
- cardreport-0.2.1/src/cardreport/html.py +185 -0
- cardreport-0.2.1/src/cardreport/model.py +408 -0
- cardreport-0.2.1/src/cardreport/output.py +25 -0
- cardreport-0.2.1/src/cardreport/pdf.py +125 -0
- cardreport-0.2.1/src/cardreport/py.typed +0 -0
- cardreport-0.2.1/src/cardreport/renderers.py +43 -0
- cardreport-0.2.1/src/cardreport/templates.py +535 -0
- cardreport-0.2.1/src/cardreport/validation.py +311 -0
- cardreport-0.2.1/src/cardreport/weasyprint.py +112 -0
- cardreport-0.2.1/tests/test_adapters.py +176 -0
- cardreport-0.2.1/tests/test_data.py +239 -0
- cardreport-0.2.1/tests/test_model.py +125 -0
- cardreport-0.2.1/tests/test_release.py +175 -0
- cardreport-0.2.1/tests/test_renderer_contract.py +393 -0
- cardreport-0.2.1/tests/test_rendering.py +266 -0
- cardreport-0.2.1/tests/test_template_reference.py +114 -0
- cardreport-0.2.1/tests/test_templates.py +286 -0
- cardreport-0.2.1/tests/typing_cases.py +66 -0
- cardreport-0.2.1/uv.lock +1823 -0
|
@@ -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.
|
cardreport-0.2.1/LICENSE
ADDED
|
@@ -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
|
+
[](https://github.com/arch7tect/cardreport/releases/latest)
|
|
32
|
+
[](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
|
+
[](https://github.com/arch7tect/cardreport/releases/latest)
|
|
4
|
+
[](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.
|