289 lines
9.4 KiB
Markdown
289 lines
9.4 KiB
Markdown
# Doris Dashboard
|
||
|
||
Local homepage for John's briefing, glanceable status, and "what matters right now" view.
|
||
|
||
## What it is
|
||
|
||
Doris Dashboard is the quiet front page for John's local information stack on NOMAD. It is not a general website and it is not meant to be another feed reader with a fresh coat of paint. It exists to turn Doris Digest output and a few homelab/task signals into one page John can open for a fast read.
|
||
|
||
The dashboard is meant to answer:
|
||
|
||
- what is worth looking at this morning
|
||
- what local or homelab signals need attention
|
||
- what chores or Paperless follow-ups are still hanging around
|
||
- what topics are surfacing too often and should be tuned down
|
||
|
||
## Why it exists
|
||
|
||
Without this stack, the raw inputs are noisy:
|
||
|
||
- Doris Digest candidates are too broad on their own
|
||
- Donetick and Paperless are useful but fragmented
|
||
- local weather/sky/homelab signals live in separate places
|
||
|
||
The dashboard turns that into one opinionated, local-only page that Doris can tune over time.
|
||
|
||
## Runtime model
|
||
|
||
- **Repo source:** `/home/fizzlepoof/repos/truenas-stacks/home/doris-dashboard`
|
||
- **Live runtime:** `/opt/doris-dashboard`
|
||
- **Generated page:** `/opt/doris-dashboard/public/index.html`
|
||
- **LAN URL:** `http://10.5.30.7:8787/`
|
||
- **Exposure rule:** local-only unless John explicitly asks otherwise
|
||
|
||
## What it shows
|
||
|
||
- top briefing cards from Doris Digest candidates
|
||
- space / homelab / local / other signal lanes
|
||
- an experimental `New to You` lane for off-profile category sampling
|
||
- Donetick tasks
|
||
- Paperless review queue items
|
||
- weather, sky, and homelab pulse cards
|
||
- lightweight stats about candidate/source mix
|
||
|
||
## How it works
|
||
|
||
- `bin/generate_dashboard.py` reads Doris Digest candidates plus a few local/runtime data files.
|
||
- It scores, deduplicates, and groups stories into dashboard sections.
|
||
- It writes generated JSON artifacts under `data/` and the final static page under `public/`.
|
||
- `bin/dashboard_server.py` serves the page and handles interactive actions like topic feedback and Paperless review state.
|
||
- The wind watchdog writes a quiet forecast notice into `~/.hermes/state/wind_alert_watchdog.json`; the dashboard surfaces that for longer-range wind events before they are close enough to deserve a direct ping.
|
||
|
||
That means the dashboard is static-looking for the reader, but still has enough local interactivity for feedback and review flows.
|
||
|
||
## Live paths
|
||
|
||
Live runtime path:
|
||
|
||
```bash
|
||
/opt/doris-dashboard
|
||
```
|
||
|
||
Generated page:
|
||
|
||
```bash
|
||
/opt/doris-dashboard/public/index.html
|
||
```
|
||
|
||
## Generate manually
|
||
|
||
```bash
|
||
cd /opt/doris-dashboard
|
||
bin/generate_dashboard.py
|
||
```
|
||
|
||
## Preview locally
|
||
|
||
```bash
|
||
cd /opt/doris-dashboard/public
|
||
python3 -m http.server 8787
|
||
```
|
||
|
||
Then open:
|
||
|
||
```text
|
||
http://localhost:8787/
|
||
```
|
||
|
||
If viewing from another machine on the LAN, use NOMAD’s LAN IP instead of localhost and make sure the firewall allows the port.
|
||
|
||
## Cron refresh
|
||
|
||
Installed in `fizzlepoof`’s crontab:
|
||
|
||
```cron
|
||
# BEGIN DORIS DASHBOARD
|
||
DORIS_DASHBOARD_DIR=/opt/doris-dashboard
|
||
DORIS_DIGEST_DIR=/home/fizzlepoof/.openclaw/workspace/doris-digest
|
||
# Refresh local dashboard after digest collection windows; wrapper enforces Chicago daytime.
|
||
5,35 * * * * cd $DORIS_DASHBOARD_DIR && bin/cron_generate_dashboard.sh >> logs/generate.log 2>&1
|
||
# END DORIS DASHBOARD
|
||
```
|
||
|
||
Inspect with:
|
||
|
||
```bash
|
||
crontab -l | sed -n '/# BEGIN DORIS DASHBOARD/,/# END DORIS DASHBOARD/p'
|
||
```
|
||
|
||
## Data inputs
|
||
|
||
Main news source:
|
||
|
||
```bash
|
||
/home/fizzlepoof/.openclaw/workspace/doris-digest/data/candidates.json
|
||
/home/fizzlepoof/.openclaw/workspace/doris-digest/data/preferences.json
|
||
```
|
||
|
||
Local dashboard placeholders:
|
||
|
||
```bash
|
||
data/todos.json
|
||
data/calendar.json
|
||
data/notes.json
|
||
```
|
||
|
||
These are intentionally simple JSON arrays so calendar/Donetick integrations can be added later without rewriting the page.
|
||
|
||
## Next integrations to add
|
||
|
||
1. Donetick tasks
|
||
2. Calendar events
|
||
3. Weather / severe weather
|
||
4. Moon phase / ISS passes / meteor shower calendar
|
||
5. Homelab health pulse
|
||
6. Article feedback buttons or quick links back to Doris
|
||
|
||
## Privacy/deployment note
|
||
|
||
This dashboard is local-only on NOMAD unless John explicitly asks to expose it. Do not publish it externally without asking.
|
||
|
||
## LAN service
|
||
|
||
The dashboard is exposed on NOMAD's LAN IP via systemd:
|
||
|
||
```text
|
||
http://10.5.30.7:8787/
|
||
```
|
||
|
||
Service file:
|
||
|
||
```bash
|
||
/etc/systemd/system/doris-dashboard.service
|
||
```
|
||
|
||
Manage it:
|
||
|
||
```bash
|
||
sudo systemctl status doris-dashboard.service
|
||
sudo systemctl restart doris-dashboard.service
|
||
sudo systemctl stop doris-dashboard.service
|
||
```
|
||
|
||
The service runs as `fizzlepoof`, serves `public/`, binds `0.0.0.0:8787`, and regenerates the page before starting.
|
||
|
||
At setup time UFW was inactive, so no firewall rule was required.
|
||
|
||
## Buildout added 2026-05-12
|
||
|
||
The dashboard generator now builds a richer local homepage directly on NOMAD.
|
||
|
||
Added sections/data:
|
||
|
||
- Weather metrics for Clarksville via Open-Meteo (`data/weather.json`)
|
||
- Moon phase / sky note calculated locally (`data/sky.json`)
|
||
- Homelab pulse checks (`data/homelab.json`): dashboard service, cron, digest freshness, workspace disk
|
||
- Article export from Doris Digest candidates (`data/articles.json`)
|
||
- Dashboard metadata (`data/dashboard.json`)
|
||
|
||
The generated page still lives at:
|
||
|
||
```bash
|
||
/opt/doris-dashboard/public/index.html
|
||
```
|
||
|
||
And the LAN service remains:
|
||
|
||
```text
|
||
http://10.5.30.7:8787/
|
||
```
|
||
|
||
Verification commands:
|
||
|
||
```bash
|
||
cd /opt/doris-dashboard
|
||
python3 -m py_compile bin/generate_dashboard.py
|
||
bin/generate_dashboard.py
|
||
curl -fsS http://127.0.0.1:8787/ | grep 'Doris Dashboard'
|
||
systemctl is-active doris-dashboard.service
|
||
crontab -l | sed -n '/# BEGIN DORIS DASHBOARD/,/# END DORIS DASHBOARD/p'
|
||
```
|
||
|
||
Notes:
|
||
|
||
- Weather.gov and NASA API are not used; Open-Meteo worked reliably without an API key.
|
||
- Donetick live task import is still not enabled in the dashboard; `data/todos.json` remains the safe placeholder until the exact read-only endpoint is confirmed.
|
||
- Raw secrets are not written to dashboard output.
|
||
|
||
## Compact news layout
|
||
|
||
2026-05-13: News/article sections were changed from large cards to compact rows so the dashboard can keep plenty of articles without making John scroll forever.
|
||
|
||
Implementation:
|
||
|
||
- `generate_dashboard.py` renders article sections with `compact-card` rows by default.
|
||
- `public/style.css` contains the compact layout rules.
|
||
- Metrics/weather/homelab pulse remain large cards.
|
||
|
||
## Donetick integration
|
||
|
||
2026-05-13: The dashboard now imports open Donetick chores read-only during generation.
|
||
|
||
Implementation notes:
|
||
|
||
- Secrets remain encrypted in `/home/fizzlepoof/.openclaw/workspace/.secrets/donetick_secrets.json.enc`.
|
||
- `bin/generate_dashboard.py` decrypts secrets at runtime using the local encryption key and logs in with the Doris service account.
|
||
- It reads `/api/v1/chores` and `/api/v1/users` only.
|
||
- It writes sanitized task data to `data/todos.json` for the dashboard.
|
||
- It does not expose tokens, passwords, or raw secrets in generated HTML/JSON.
|
||
|
||
Verify:
|
||
|
||
```bash
|
||
cd /opt/doris-dashboard
|
||
bin/generate_dashboard.py
|
||
python3 - <<'PY'
|
||
import json
|
||
from pathlib import Path
|
||
for item in json.loads(Path('data/todos.json').read_text()):
|
||
print(item.get('time'), item.get('title'), item.get('assignee'))
|
||
PY
|
||
```
|
||
|
||
## Donetick compact board + topic feedback
|
||
|
||
2026-05-14: The dashboard's Donetick section was reworked to match the tighter PD Dakboard pattern more closely.
|
||
|
||
Current behavior:
|
||
|
||
- Donetick renders compact task cards with due cue, task title, label chips, and a short subtitle.
|
||
- Tasks are sorted by urgency first (overdue/today/upcoming/no due date), then due time.
|
||
- The dashboard injects a pinned Paperless follow-up card into the Donetick area so that cleanup work stays visible.
|
||
- News cards now show `👍 / 👎` topic buttons.
|
||
- Topic feedback is persisted through the local dashboard server into `../doris-digest/data/feedback.json` with `origin: "dashboard"`.
|
||
- `Reset hidden topics` clears both browser state and dashboard-origin feedback entries.
|
||
- Paperless review cards now have `Acknowledge` / `Dismiss` actions persisted to `data/paperless_review_state.json`.
|
||
- Major dashboard sections are collapsible and remember collapsed state in the browser.
|
||
|
||
Important note:
|
||
|
||
- The interactive actions require the dashboard to be served by `bin/dashboard_server.py`, not plain `python -m http.server`.
|
||
|
||
## Runtime path sanity
|
||
|
||
The live systemd unit and crontab must point only at:
|
||
|
||
```bash
|
||
/opt/doris-dashboard
|
||
```
|
||
|
||
Do not run the dashboard from an agent workspace. Agent workspaces are for editing and staging; NOMAD host-run services belong under `/opt/<service>`.
|
||
|
||
## Cron fix — 2026-05-13
|
||
|
||
The dashboard cron now uses `bin/cron_generate_dashboard.sh`, which checks America/Chicago daytime internally. This avoids relying on `CRON_TZ`, which NOMAD's cron did not honor consistently.
|
||
|
||
## Paperless review queue contract
|
||
|
||
The dashboard displays sanitized Paperless intake triage items written by n8n at `data/paperless_review.json`. Example contract: `data/paperless_review.example.json`.
|
||
|
||
Expected shape is a JSON array. Each item may include:
|
||
|
||
- `id`, `title`, `original_filename`, `added`, `archive_url`
|
||
- `current.correspondent`, `current.document_type`, `current.tags`
|
||
- `triage.summary`, `triage.suggested_title`, `triage.document_type`, `triage.suggested_tags`, `triage.due_date`, `triage.needs_review`, `triage.urgency`, `triage.reason`, `triage.confidence`
|
||
- `safety.forced_review`, `safety.eligible_for_safe_apply`, `safety.apply_safe_enabled`
|
||
- `queued_at`
|
||
|
||
Do not write tokens, raw OCR text, API responses, or credentials into this file. It is display-only derived data for local review.
|