Skip to content

Commit 05db8be

Browse files
Yerazeclaude
andauthored
feat(analysis): NodeInfo Enrichment report under Analysis & Reports (#3837 Phase 2) (#4289)
* refactor(nodes): extract shared NodeInfo field labels for enrichment report (#3837 WP-1) Extract CopyNodeInfoModal's local DISPLAY_FIELDS label map into a shared src/utils/nodeInfoFields.ts module (NODE_INFO_DISPLAY_FIELDS, NODE_INFO_FIELD_LABELS, nodeInfoFieldLabel, NodeInfoFieldKey) so the upcoming NodeInfo Enrichment report and the modal share one source of truth. The modal imports the list aliased as DISPLAY_FIELDS — zero behavior or rendering change. Also add the additive enrichment-report CSS (.reports-enrichment__push, .reports-field-pill) to src/styles/analysis-reports.css for WP-2. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018e4dtLyWeYJYJ7SbgFvGG1 * feat(analysis): NodeInfo Enrichment report + AnalysisTab registration (#3837 Phase 2 WP-2) Adds the frontend report component that consumes Phase 1's GET/POST /api/nodes/enrichment/{analysis,apply} endpoints: flattens (node,target) pairs into table rows, per-row Fix + Fix All via one mutation, a push-to-NodeDB toggle (default off), and loading/error/empty/ populated states. Registers the card in AnalysisTab's reports grid. Also commits the Phase 2 spec doc (was untracked in the worktree). * docs: document NodeInfo Enrichment report on Analysis & Reports page Adds a NodeInfo Enrichment section (#3837) alongside Solar Monitoring on the Analysis & Reports page, covering what gets analyzed, the fill-blanks-only Fix/Fix All apply model, the push-to-device toggle, and per-endpoint permissions and error codes. * docs: mark epic Phase 2 implementation complete with deviations (#3837) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018e4dtLyWeYJYJ7SbgFvGG1 --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent c46bc47 commit 05db8be

9 files changed

Lines changed: 1150 additions & 19 deletions

File tree

docs/features/analysis-reports.md

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
The **Analysis & Reports** workspace is a global, cross-source analytics page that runs analytical reports against the telemetry, position, and routing data MeshMonitor has collected from every source you can read. It lives at `/reports` and is linked from the bottom of the dashboard sidebar (right under *Map Analysis*).
44

5-
The first report bundled with the workspace is **Solar Monitoring Analysis** — see below. The card-grid landing page is designed to host additional reports over time without changing the routing or navigation surface.
5+
Two reports are currently bundled with the workspace: **Solar Monitoring Analysis** and **NodeInfo Enrichment** — see below. The card-grid landing page is designed to host additional reports over time without changing the routing or navigation surface.
66

77
## Solar Monitoring Analysis
88

@@ -60,25 +60,72 @@ Nodes whose simulated minimum drops below `50%` (battery) or `3.5 V` (voltage) a
6060

6161
Solar production must already be configured under **Settings → Solar Monitoring** for the forecast to produce useful output. See the [Solar Monitoring guide](./solar-monitoring) for set-up details.
6262

63+
## NodeInfo Enrichment
64+
65+
Scans every node visible on more than one source for blank NodeInfo fields — Long Name, Short Name, Hardware Model, Role, MAC Address, Public Key, Firmware — that another source's record for the same physical node can fill, and lets you copy the missing data across sources. It generalizes the per-node "Copy NodeInfo" dialog (available from the node detail panel) into a batch view spanning every source you can read.
66+
67+
### What gets analyzed
68+
69+
For every node seen on two or more of your permitted sources, the report checks the fields above for blanks. Each row in the report table is one **(node, target source)** pair — a specific source's record for a specific node that is missing at least one field — and lists:
70+
71+
- The **fillable fields** for that row (only the ones actually blank on the target and present on the donor)
72+
- The **donor source**: whichever other source holds the most complete record for that node. Completeness is ranked by number of non-blank fields, with the most recently updated record winning ties.
73+
74+
### Running the report
75+
76+
1. Open the dashboard, click **Analysis & Reports** in the sidebar.
77+
2. Click the **NodeInfo Enrichment** card.
78+
3. Review the summary tiles — **Nodes**, **Targets**, **Fillable fields** — above the table.
79+
4. Click **Fix** on a row to apply just that (node, target source) pair, or **Fix All** to apply every row currently listed.
80+
5. Click **Refresh** to re-run the analysis, e.g. after applying fixes or after new NodeInfo has arrived over the mesh.
81+
82+
### Applying fixes
83+
84+
Applying a row is **fill-blanks-only**: it writes only the fields that are currently empty on the target source's record and never overwrites a field that already has a value — even if the analysis backing the row has gone stale since the page loaded. This makes Fix and Fix All safe to click at any time.
85+
86+
An **Also push to device NodeDB** toggle (default off) sits above the table. When enabled, each node that gets enriched also receives the same NodeInfo-exchange nudge as the manual Copy NodeInfo dialog's push option (`sendNodeInfoRequest` on the node's channel). This generates mesh traffic, so leave it off if you only want to update MeshMonitor's own database.
87+
88+
Because a row reflects only its single best donor, fixing it can uncover further blanks: if that donor didn't have every missing field, the row disappears but the node may resurface with a different (partial) donor on the next analysis. Click **Refresh** (or re-run the report) until the table drains to fully converge a node across all sources.
89+
90+
> **On-demand only.** This report has no background scheduler — it runs interactively whenever you open the card or click Refresh/Fix.
91+
6392
## Permissions
6493

65-
Both endpoints behind the workspace are scoped to the requesting user's permitted source IDs:
94+
### Solar Monitoring
95+
96+
Both solar endpoints are scoped to the requesting user's permitted source IDs:
6697

6798
- Admins see all enabled sources
6899
- Non-admin users see sources where they hold `nodes:read`
69100
- An optional `?sources=id1,id2` query param restricts the analysis further (intersected with permitted IDs)
70101

71-
The page itself is publicly routable; only the data is gated.
102+
### NodeInfo Enrichment
103+
104+
- **Analysis** (`GET /api/nodes/enrichment/analysis`) only ever computes over sources the caller holds `nodes:read` on — admins see every source, anonymous/unauthenticated callers see none (an empty, but never a `403`, response).
105+
- **Applying** (`POST /api/nodes/enrichment/apply`) requires `nodes:read` on the donor source and `nodes:write` on every target source referenced by the request. A single missing grant rejects the whole batch with `403 FORBIDDEN` — partial application of an under-permissioned batch never happens.
106+
107+
The page itself is publicly routable for both reports; only the underlying data is gated.
72108

73109
## API
74110

75-
The Reports workspace surfaces two endpoints under the existing `/api/analysis/*` namespace:
111+
### Solar Monitoring
112+
113+
Two endpoints under the existing `/api/analysis/*` namespace:
76114

77115
- `GET /api/analysis/solar-nodes?lookback_days=N&sources=…`
78116
- `GET /api/analysis/solar-forecast?lookback_days=N&sources=…`
79117

80118
See the [REST API reference](https://github.com/Yeraze/meshmonitor/blob/main/docs/api/REST_API.md) for the full request/response shapes.
81119

120+
### NodeInfo Enrichment
121+
122+
Two endpoints under `/api/nodes/enrichment/*` (also mirrored at `/api/v1/nodes/enrichment/*`), using the standard `{ success, data }` response envelope:
123+
124+
- `GET /api/nodes/enrichment/analysis` — returns `{ nodes: [...], summary: { nodeCount, targetCount, fieldCount } }`.
125+
- `POST /api/nodes/enrichment/apply` — body `{ items: [{ nodeNum, targetSourceId, donorSourceId }], pushToNodeDb? }`, returns `{ applied: [...], totalFieldsCopied }`.
126+
127+
Error codes: `INVALID_REQUEST` / `INVALID_ITEM` (400, malformed request body), `FORBIDDEN` (403, missing `nodes:read`/`nodes:write` — includes a `missing` list), `ENRICHMENT_ANALYSIS_FAILED` / `ENRICHMENT_APPLY_FAILED` (500).
128+
82129
## Related
83130

84131
- [Solar Monitoring](./solar-monitoring) — configuration of the forecast.solar integration that powers the production curve and forecast factor

docs/internal/dev-notes/NODEINFO_ENRICHMENT_EPIC.md

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,19 +58,31 @@ apply the enrichment ("Fix") per node or in bulk.
5858
### Phase 2 — Frontend: Analysis & Reports card
5959
**Branch:** `feature/nodeinfo-enrichment-report`
6060

61-
- [ ] Extend `AnalysisType` + `reports[]` with a "NodeInfo Enrichment" card; new
61+
- [x] Extend `AnalysisType` + `reports[]` with a "NodeInfo Enrichment" card; new
6262
`src/components/Analysis/NodeInfoEnrichmentReport.tsx` (TanStack useQuery +
6363
`ApiService` — raw fetch is banned in new components; `UiIcon` for icons).
64-
- [ ] Table of enrichable nodes (node, target source(s), missing fields, donor source),
64+
- [x] Table of enrichable nodes (node, target source(s), missing fields, donor source),
6565
per-row **Fix** + **Fix All**, push-to-NodeDB toggle default off, empty/loading/
6666
unauthorized states.
67-
- [ ] Component tests; browser validation in the dev container (screenshots, console
67+
- [x] Component tests; browser validation in the dev container (screenshots, console
6868
clean, gauntlet-channel only for any test traffic).
6969
- **Exit criteria:** report usable end-to-end against the dev container; suite green;
7070
PR merged to main; issue #3837 closed.
7171

7272
## Deviations / notes
7373

74+
- Phase 2: a row's `fillableFields` reflect its single best donor, so fixing a row can
75+
surface remaining blanks under a different donor on the next analysis (observed live:
76+
fill Firmware from Sandbox → MAC Address appears with donor Florida MQTT). By design;
77+
documented in the feature doc ("run again until the list drains").
78+
- Phase 2: shared field-label map extracted to `src/utils/nodeInfoFields.ts`;
79+
`CopyNodeInfoModal` refactored to consume it (zero behavior change).
80+
- Phase 2: anonymous users see a non-empty analysis when the anonymous account holds
81+
`nodes:read` grants (validated live: read-narrowed analysis + apply 403 fail-closed
82+
with `missing` list). The empty-state hint covers the no-grants case.
83+
- Phase 2: component test uses a file-local `react-i18next` mock (the global test setup
84+
mocks the 2-arg `t()` form; the component uses the 3-arg default-value form).
85+
7486
- Phase 1: apply endpoint takes an explicit `items` list
7587
(`{nodeNum, targetSourceId, donorSourceId}[]`) so Phase 2's per-row Fix and
7688
Fix All share one endpoint; fill-blanks-only is enforced by delegating to

0 commit comments

Comments
 (0)