epidash 0.2.0__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.
- epidash-0.2.0/.github/workflows/ci.yml +26 -0
- epidash-0.2.0/.github/workflows/publish.yml +55 -0
- epidash-0.2.0/.gitignore +23 -0
- epidash-0.2.0/Dockerfile +27 -0
- epidash-0.2.0/LICENSE +21 -0
- epidash-0.2.0/PKG-INFO +153 -0
- epidash-0.2.0/README.md +135 -0
- epidash-0.2.0/docker-compose.yml +27 -0
- epidash-0.2.0/epidash/__init__.py +3 -0
- epidash-0.2.0/epidash/api/__init__.py +1 -0
- epidash-0.2.0/epidash/api/routes_public.py +284 -0
- epidash-0.2.0/epidash/cli.py +76 -0
- epidash-0.2.0/epidash/config.py +113 -0
- epidash-0.2.0/epidash/epidbot_client.py +152 -0
- epidash-0.2.0/epidash/logging.py +22 -0
- epidash-0.2.0/epidash/main.py +133 -0
- epidash-0.2.0/epidash/mcpsrv/__init__.py +1 -0
- epidash-0.2.0/epidash/mcpsrv/audit.py +44 -0
- epidash-0.2.0/epidash/mcpsrv/auth.py +104 -0
- epidash-0.2.0/epidash/mcpsrv/server.py +406 -0
- epidash-0.2.0/epidash/schema.sql +78 -0
- epidash-0.2.0/epidash/static/app.js +739 -0
- epidash-0.2.0/epidash/static/epidbot.webp +0 -0
- epidash-0.2.0/epidash/static/geo/br-states.json +1 -0
- epidash-0.2.0/epidash/static/index.html +37 -0
- epidash-0.2.0/epidash/static/kwar-ai-logo.webp +0 -0
- epidash-0.2.0/epidash/static/style.css +314 -0
- epidash-0.2.0/epidash/static/vendor/plotly.min.js +3882 -0
- epidash-0.2.0/epidash/store.py +503 -0
- epidash-0.2.0/pyproject.toml +46 -0
- epidash-0.2.0/tests/conftest.py +58 -0
- epidash-0.2.0/tests/test_mcp_auth.py +89 -0
- epidash-0.2.0/tests/test_mcp_tools.py +285 -0
- epidash-0.2.0/tests/test_public_api.py +139 -0
- epidash-0.2.0/tests/test_store.py +316 -0
- epidash-0.2.0/tests/test_tweak_flow.py +243 -0
- epidash-0.2.0/uv.lock +1232 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
|
|
14
|
+
- name: Install uv
|
|
15
|
+
uses: astral-sh/setup-uv@v5
|
|
16
|
+
with:
|
|
17
|
+
python-version: '3.12'
|
|
18
|
+
|
|
19
|
+
- name: Sync dependencies
|
|
20
|
+
run: uv sync --frozen
|
|
21
|
+
|
|
22
|
+
- name: Type check
|
|
23
|
+
run: uv run ty check epidash/
|
|
24
|
+
|
|
25
|
+
- name: Run tests
|
|
26
|
+
run: uv run pytest -v
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ['v*']
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
|
|
14
|
+
- name: Install uv
|
|
15
|
+
uses: astral-sh/setup-uv@v5
|
|
16
|
+
with:
|
|
17
|
+
python-version: '3.12'
|
|
18
|
+
|
|
19
|
+
- name: Sync dependencies
|
|
20
|
+
run: uv sync --frozen
|
|
21
|
+
|
|
22
|
+
- name: Type check
|
|
23
|
+
run: uv run ty check epidash/
|
|
24
|
+
|
|
25
|
+
- name: Run tests
|
|
26
|
+
run: uv run pytest -v
|
|
27
|
+
|
|
28
|
+
publish:
|
|
29
|
+
needs: test
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
environment: pypi
|
|
32
|
+
permissions:
|
|
33
|
+
id-token: write # OIDC trusted publishing — no API token secret needed
|
|
34
|
+
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@v4
|
|
37
|
+
|
|
38
|
+
- name: Install uv
|
|
39
|
+
uses: astral-sh/setup-uv@v5
|
|
40
|
+
with:
|
|
41
|
+
python-version: '3.12'
|
|
42
|
+
|
|
43
|
+
- name: Verify tag matches package version
|
|
44
|
+
run: |
|
|
45
|
+
VERSION=$(uv run --no-sync python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
|
46
|
+
if [ "v$VERSION" != "$GITHUB_REF_NAME" ]; then
|
|
47
|
+
echo "::error::Tag $GITHUB_REF_NAME does not match package version v$VERSION"
|
|
48
|
+
exit 1
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
- name: Build package
|
|
52
|
+
run: uv build
|
|
53
|
+
|
|
54
|
+
- name: Publish to PyPI
|
|
55
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
epidash-0.2.0/.gitignore
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.venv/
|
|
6
|
+
venv/
|
|
7
|
+
dist/
|
|
8
|
+
build/
|
|
9
|
+
|
|
10
|
+
# Local state
|
|
11
|
+
data/
|
|
12
|
+
*.db
|
|
13
|
+
*.db-wal
|
|
14
|
+
*.db-shm
|
|
15
|
+
|
|
16
|
+
# Tooling
|
|
17
|
+
.pytest_cache/
|
|
18
|
+
.ty/
|
|
19
|
+
.ruff_cache/
|
|
20
|
+
|
|
21
|
+
# Secrets
|
|
22
|
+
.env
|
|
23
|
+
.env.local
|
epidash-0.2.0/Dockerfile
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
FROM python:3.12-slim
|
|
2
|
+
|
|
3
|
+
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /usr/local/bin/
|
|
4
|
+
|
|
5
|
+
WORKDIR /app
|
|
6
|
+
|
|
7
|
+
# Install dependencies first (cached layer), then the app code
|
|
8
|
+
COPY pyproject.toml uv.lock README.md ./
|
|
9
|
+
RUN uv sync --frozen --no-dev --no-install-project
|
|
10
|
+
|
|
11
|
+
COPY epidash ./epidash
|
|
12
|
+
RUN uv sync --frozen --no-dev
|
|
13
|
+
|
|
14
|
+
ENV PATH="/app/.venv/bin:$PATH" \
|
|
15
|
+
EPIDASH_DB_PATH=/data/epidash.db
|
|
16
|
+
|
|
17
|
+
RUN useradd --system --create-home epidash \
|
|
18
|
+
&& mkdir -p /data \
|
|
19
|
+
&& chown -R epidash:epidash /app /data
|
|
20
|
+
USER epidash
|
|
21
|
+
|
|
22
|
+
EXPOSE 8000
|
|
23
|
+
|
|
24
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
|
25
|
+
CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/healthz', timeout=4)"
|
|
26
|
+
|
|
27
|
+
CMD ["uvicorn", "epidash.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
epidash-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Deeplearn-PeD
|
|
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.
|
epidash-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: epidash
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: EpiDash — light MCP-controlled dashboard service: EpidBot publishes and controls dashboards via MCP
|
|
5
|
+
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.12
|
|
8
|
+
Requires-Dist: fastapi>=0.115.0
|
|
9
|
+
Requires-Dist: httpx>=0.28.0
|
|
10
|
+
Requires-Dist: mcp<2,>=1.10
|
|
11
|
+
Requires-Dist: pydantic>=2.9
|
|
12
|
+
Requires-Dist: uvicorn[standard]>=0.32.0
|
|
13
|
+
Provides-Extra: dev
|
|
14
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
|
|
15
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
16
|
+
Requires-Dist: ty>=0.0.1a5; extra == 'dev'
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# EpiDash
|
|
20
|
+
|
|
21
|
+
[](https://github.com/Deeplearn-PeD/epidash/actions/workflows/ci.yml)
|
|
22
|
+
[](https://www.python.org/downloads/)
|
|
23
|
+
[](LICENSE)
|
|
24
|
+
[](https://docs.astral.sh/uv/)
|
|
25
|
+
|
|
26
|
+
A light, MCP-controlled dashboard service. [EpidBot](https://github.com/Deeplearn-PeD/EpiDBot)
|
|
27
|
+
(and any MCP client holding a control key) builds, updates, publishes and
|
|
28
|
+
unpublishes dashboards through an MCP tool surface; anyone with a dashboard
|
|
29
|
+
link views it in a minimal web UI. Viewers can ask EpidBot to tweak a
|
|
30
|
+
dashboard from the browser.
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
EpidBot agent ──(MCP client, dk_ key)──▶ EpiDash /mcp (FastMCP tools)
|
|
34
|
+
│
|
|
35
|
+
SQLite ◀─────────────┤ (dashboards, charts, keys, audit)
|
|
36
|
+
│
|
|
37
|
+
Viewer browser ──(public link)──▶ EpiDash / (vanilla JS + Plotly)
|
|
38
|
+
│ │
|
|
39
|
+
└── tweak request ──▶ POST /api/dashboards/{slug}/tweak
|
|
40
|
+
│ (X-Internal-Request: shared secret)
|
|
41
|
+
▼
|
|
42
|
+
EpidBot /api/v1/chat ──agent──▶ EpiDash MCP tools
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Quick start
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
uv sync
|
|
49
|
+
uv run uvicorn epidash.main:app --port 8000
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Then issue a control key and talk to the MCP endpoint:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
uv run python -m epidash.cli add-key "local"
|
|
56
|
+
# → dk_... (shown once)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# The MCP endpoint is stateless: one POST per call, no handshake needed.
|
|
61
|
+
curl -s -X POST http://localhost:8000/mcp \
|
|
62
|
+
-H "X-API-Key: dk_..." -H "Content-Type: application/json" \
|
|
63
|
+
-H "Accept: application/json, text/event-stream" \
|
|
64
|
+
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
In an MCP client (e.g. Claude Desktop, Cursor, EpidBot):
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"mcpServers": {
|
|
72
|
+
"epidash": {
|
|
73
|
+
"url": "http://localhost:8000/mcp",
|
|
74
|
+
"headers": { "X-API-Key": "dk_..." }
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Configuration (environment variables)
|
|
81
|
+
|
|
82
|
+
| Variable | Default | Purpose |
|
|
83
|
+
|---|---|---|
|
|
84
|
+
| `EPIDASH_DB_PATH` | `data/epidash.db` | SQLite file (WAL) |
|
|
85
|
+
| `EPIDASH_BASE_URL` | `http://localhost:8000` | Public base URL for share links |
|
|
86
|
+
| `EPIDASH_MCP_PATH` | `/mcp` | Mount path of the MCP endpoint |
|
|
87
|
+
| `EPIDASH_ALLOWED_HOSTS` | `localhost,127.0.0.1` | Host header allowlist (DNS-rebinding protection) |
|
|
88
|
+
| `EPIDASH_BOOTSTRAP_KEY` | — | Pre-shared `dk_` key seeded at startup (infra automation) |
|
|
89
|
+
| `EPIDBOT_BASE_URL` | — | EpidBot base URL; enables the tweak flow |
|
|
90
|
+
| `EPIDBOT_INTERNAL_SECRET` | — | Shared secret (EpidBot `INTERNAL_API_SECRET`) or `ek_` API key |
|
|
91
|
+
| `EPIDASH_TWEAK_RATE_LIMIT_PER_HOUR` | `20` | Per-dashboard viewer tweak budget |
|
|
92
|
+
| `EPIDASH_ASK_RATE_LIMIT_PER_HOUR` | `30` | Per-dashboard viewer question budget |
|
|
93
|
+
| `EPIDASH_TWEAK_TIMEOUT_S` | `300` | Max wait for an EpidBot tweak turn |
|
|
94
|
+
|
|
95
|
+
## MCP tools
|
|
96
|
+
|
|
97
|
+
Read-only: `list_dashboards`, `get_dashboard`, `get_view_stats`, `get_audit_log`.
|
|
98
|
+
|
|
99
|
+
Mutations (audited): `create_dashboard`, `update_dashboard`,
|
|
100
|
+
`publish_dashboard` (confirm), `unpublish_dashboard` (confirm),
|
|
101
|
+
`add_chart`, `update_chart`, `remove_chart` (confirm).
|
|
102
|
+
|
|
103
|
+
### Chart data envelopes
|
|
104
|
+
|
|
105
|
+
| kind | data |
|
|
106
|
+
|---|---|
|
|
107
|
+
| `line`, `bar`, `pie`, `table` | `{"columns": ["month", "cases"], "rows": [["Jan", 120], ...]}` |
|
|
108
|
+
| `kpi` | `{"value": 1234, "label": "Cases", "unit": "", "delta": "+12%"}` |
|
|
109
|
+
| `choropleth` | `{"values": {"SP": 120, "RJ": 90}, "region_level": "uf", "label": "cases"}` |
|
|
110
|
+
|
|
111
|
+
`spec` accepts style overrides (`{"color": "#4ade80"}`, `{"y_label": "cases"}`) and
|
|
112
|
+
`position` controls ordering/width (`{"order": 1, "width": "half"}`).
|
|
113
|
+
|
|
114
|
+
## Viewer tweak flow
|
|
115
|
+
|
|
116
|
+
A viewer types a request ("show only the Southeast after 2020") into the
|
|
117
|
+
dashboard's chat box. EpiDash forwards it as an EpidBot chat turn with the
|
|
118
|
+
dashboard context; the EpidBot agent uses its EpiDash MCP tools to update
|
|
119
|
+
the charts; the viewer's browser picks up the change on its next poll.
|
|
120
|
+
|
|
121
|
+
Viewers can also **ask questions** ("why did cases spike in March?") via
|
|
122
|
+
the Ask button — the same chat pipeline, but the prompt instructs EpidBot
|
|
123
|
+
to answer from the dashboard data without modifying it. No authentication
|
|
124
|
+
is required for either; questions are recorded in the `tweaks` table with
|
|
125
|
+
`kind='question'` and rate-limited separately from tweaks.
|
|
126
|
+
|
|
127
|
+
Requires `EPIDBOT_BASE_URL` + `EPIDBOT_INTERNAL_SECRET` (EpidBot must accept
|
|
128
|
+
`X-Internal-Request` on its chat endpoint — see the EpidBot-side integration
|
|
129
|
+
docs) or an `ek_` API key.
|
|
130
|
+
|
|
131
|
+
## Deployment
|
|
132
|
+
|
|
133
|
+
Single container, single volume:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
docker compose up -d --build
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Production (KwarAI) deploys this via the Ansible `epidash` role behind
|
|
140
|
+
nginx at `dash.kwar-ai.com.br` — see the
|
|
141
|
+
[infrastructure repo](https://github.com/Deeplearn-PeD/infrastructure).
|
|
142
|
+
|
|
143
|
+
## Development
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
uv sync # deps
|
|
147
|
+
uv run pytest -v # tests
|
|
148
|
+
uv run ty check epidash/ # type check
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## License
|
|
152
|
+
|
|
153
|
+
MIT
|
epidash-0.2.0/README.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# EpiDash
|
|
2
|
+
|
|
3
|
+
[](https://github.com/Deeplearn-PeD/epidash/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.python.org/downloads/)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://docs.astral.sh/uv/)
|
|
7
|
+
|
|
8
|
+
A light, MCP-controlled dashboard service. [EpidBot](https://github.com/Deeplearn-PeD/EpiDBot)
|
|
9
|
+
(and any MCP client holding a control key) builds, updates, publishes and
|
|
10
|
+
unpublishes dashboards through an MCP tool surface; anyone with a dashboard
|
|
11
|
+
link views it in a minimal web UI. Viewers can ask EpidBot to tweak a
|
|
12
|
+
dashboard from the browser.
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
EpidBot agent ──(MCP client, dk_ key)──▶ EpiDash /mcp (FastMCP tools)
|
|
16
|
+
│
|
|
17
|
+
SQLite ◀─────────────┤ (dashboards, charts, keys, audit)
|
|
18
|
+
│
|
|
19
|
+
Viewer browser ──(public link)──▶ EpiDash / (vanilla JS + Plotly)
|
|
20
|
+
│ │
|
|
21
|
+
└── tweak request ──▶ POST /api/dashboards/{slug}/tweak
|
|
22
|
+
│ (X-Internal-Request: shared secret)
|
|
23
|
+
▼
|
|
24
|
+
EpidBot /api/v1/chat ──agent──▶ EpiDash MCP tools
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick start
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
uv sync
|
|
31
|
+
uv run uvicorn epidash.main:app --port 8000
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Then issue a control key and talk to the MCP endpoint:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
uv run python -m epidash.cli add-key "local"
|
|
38
|
+
# → dk_... (shown once)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# The MCP endpoint is stateless: one POST per call, no handshake needed.
|
|
43
|
+
curl -s -X POST http://localhost:8000/mcp \
|
|
44
|
+
-H "X-API-Key: dk_..." -H "Content-Type: application/json" \
|
|
45
|
+
-H "Accept: application/json, text/event-stream" \
|
|
46
|
+
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
In an MCP client (e.g. Claude Desktop, Cursor, EpidBot):
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"mcpServers": {
|
|
54
|
+
"epidash": {
|
|
55
|
+
"url": "http://localhost:8000/mcp",
|
|
56
|
+
"headers": { "X-API-Key": "dk_..." }
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Configuration (environment variables)
|
|
63
|
+
|
|
64
|
+
| Variable | Default | Purpose |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| `EPIDASH_DB_PATH` | `data/epidash.db` | SQLite file (WAL) |
|
|
67
|
+
| `EPIDASH_BASE_URL` | `http://localhost:8000` | Public base URL for share links |
|
|
68
|
+
| `EPIDASH_MCP_PATH` | `/mcp` | Mount path of the MCP endpoint |
|
|
69
|
+
| `EPIDASH_ALLOWED_HOSTS` | `localhost,127.0.0.1` | Host header allowlist (DNS-rebinding protection) |
|
|
70
|
+
| `EPIDASH_BOOTSTRAP_KEY` | — | Pre-shared `dk_` key seeded at startup (infra automation) |
|
|
71
|
+
| `EPIDBOT_BASE_URL` | — | EpidBot base URL; enables the tweak flow |
|
|
72
|
+
| `EPIDBOT_INTERNAL_SECRET` | — | Shared secret (EpidBot `INTERNAL_API_SECRET`) or `ek_` API key |
|
|
73
|
+
| `EPIDASH_TWEAK_RATE_LIMIT_PER_HOUR` | `20` | Per-dashboard viewer tweak budget |
|
|
74
|
+
| `EPIDASH_ASK_RATE_LIMIT_PER_HOUR` | `30` | Per-dashboard viewer question budget |
|
|
75
|
+
| `EPIDASH_TWEAK_TIMEOUT_S` | `300` | Max wait for an EpidBot tweak turn |
|
|
76
|
+
|
|
77
|
+
## MCP tools
|
|
78
|
+
|
|
79
|
+
Read-only: `list_dashboards`, `get_dashboard`, `get_view_stats`, `get_audit_log`.
|
|
80
|
+
|
|
81
|
+
Mutations (audited): `create_dashboard`, `update_dashboard`,
|
|
82
|
+
`publish_dashboard` (confirm), `unpublish_dashboard` (confirm),
|
|
83
|
+
`add_chart`, `update_chart`, `remove_chart` (confirm).
|
|
84
|
+
|
|
85
|
+
### Chart data envelopes
|
|
86
|
+
|
|
87
|
+
| kind | data |
|
|
88
|
+
|---|---|
|
|
89
|
+
| `line`, `bar`, `pie`, `table` | `{"columns": ["month", "cases"], "rows": [["Jan", 120], ...]}` |
|
|
90
|
+
| `kpi` | `{"value": 1234, "label": "Cases", "unit": "", "delta": "+12%"}` |
|
|
91
|
+
| `choropleth` | `{"values": {"SP": 120, "RJ": 90}, "region_level": "uf", "label": "cases"}` |
|
|
92
|
+
|
|
93
|
+
`spec` accepts style overrides (`{"color": "#4ade80"}`, `{"y_label": "cases"}`) and
|
|
94
|
+
`position` controls ordering/width (`{"order": 1, "width": "half"}`).
|
|
95
|
+
|
|
96
|
+
## Viewer tweak flow
|
|
97
|
+
|
|
98
|
+
A viewer types a request ("show only the Southeast after 2020") into the
|
|
99
|
+
dashboard's chat box. EpiDash forwards it as an EpidBot chat turn with the
|
|
100
|
+
dashboard context; the EpidBot agent uses its EpiDash MCP tools to update
|
|
101
|
+
the charts; the viewer's browser picks up the change on its next poll.
|
|
102
|
+
|
|
103
|
+
Viewers can also **ask questions** ("why did cases spike in March?") via
|
|
104
|
+
the Ask button — the same chat pipeline, but the prompt instructs EpidBot
|
|
105
|
+
to answer from the dashboard data without modifying it. No authentication
|
|
106
|
+
is required for either; questions are recorded in the `tweaks` table with
|
|
107
|
+
`kind='question'` and rate-limited separately from tweaks.
|
|
108
|
+
|
|
109
|
+
Requires `EPIDBOT_BASE_URL` + `EPIDBOT_INTERNAL_SECRET` (EpidBot must accept
|
|
110
|
+
`X-Internal-Request` on its chat endpoint — see the EpidBot-side integration
|
|
111
|
+
docs) or an `ek_` API key.
|
|
112
|
+
|
|
113
|
+
## Deployment
|
|
114
|
+
|
|
115
|
+
Single container, single volume:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
docker compose up -d --build
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Production (KwarAI) deploys this via the Ansible `epidash` role behind
|
|
122
|
+
nginx at `dash.kwar-ai.com.br` — see the
|
|
123
|
+
[infrastructure repo](https://github.com/Deeplearn-PeD/infrastructure).
|
|
124
|
+
|
|
125
|
+
## Development
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
uv sync # deps
|
|
129
|
+
uv run pytest -v # tests
|
|
130
|
+
uv run ty check epidash/ # type check
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## License
|
|
134
|
+
|
|
135
|
+
MIT
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
services:
|
|
2
|
+
epidash:
|
|
3
|
+
build: .
|
|
4
|
+
image: epidash:latest
|
|
5
|
+
container_name: epidash
|
|
6
|
+
ports:
|
|
7
|
+
- '127.0.0.1:8310:8000'
|
|
8
|
+
volumes:
|
|
9
|
+
- ./data:/data
|
|
10
|
+
environment:
|
|
11
|
+
- EPIDASH_DB_PATH=/data/epidash.db
|
|
12
|
+
- EPIDASH_BASE_URL=${EPIDASH_BASE_URL:-http://localhost:8310}
|
|
13
|
+
- EPIDASH_ALLOWED_HOSTS=${EPIDASH_ALLOWED_HOSTS:-localhost,127.0.0.1}
|
|
14
|
+
- EPIDASH_BOOTSTRAP_KEY=${EPIDASH_BOOTSTRAP_KEY:-}
|
|
15
|
+
- EPIDBOT_BASE_URL=${EPIDBOT_BASE_URL:-}
|
|
16
|
+
- EPIDBOT_INTERNAL_SECRET=${EPIDBOT_INTERNAL_SECRET:-}
|
|
17
|
+
restart: unless-stopped
|
|
18
|
+
healthcheck:
|
|
19
|
+
test:
|
|
20
|
+
- CMD
|
|
21
|
+
- python
|
|
22
|
+
- -c
|
|
23
|
+
- "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/healthz', timeout=4)"
|
|
24
|
+
interval: 30s
|
|
25
|
+
timeout: 5s
|
|
26
|
+
retries: 3
|
|
27
|
+
start_period: 10s
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Public REST API: published dashboards + the viewer tweak flow."""
|