agmission/client/docs/ADVANCED_REPORTS_IMPLEMENTATION_PLAN.md

105 lines
9.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Advanced Reports — Implementation Plan (Phase 1)
**Version:** 1.0
**Date:** July 9, 2026
**Status:** Draft — derived from the approved planning set
**Related Documents:** `ADVANCED_REPORTS_FEASIBILITY.md`, `ADVANCED_REPORTS_FUNCTIONAL.md`, `ADVANCED_REPORTS_NON_FUNCTIONAL.md`, `ADVANCED_REPORTS_API.md`, `ADVANCED_REPORTS_PROPOSAL.md`
---
## 1. Approach
Build the Advanced Application Report on the existing report pipeline: the server computes analytics, renders map images, and writes a datasource; the client Stimulsoft viewer renders and exports. One analytics engine feeds every page (FR-5.4); content options are applied by shaping the datasource, not by swapping templates.
Guiding principles:
- **Pure-function analytics core** — testable without HTTP, Mongo, or Puppeteer (NFR-6.3).
- **Legacy untouched** — no shared-code change may alter `preAppReport`/loadsheet behaviour (NFR-5.1).
- **`ApplicationDetail` is read once per report** (single pass, streaming cursor, projected fields only — NFR-1.2/1.4).
- **One Chromium instance** per report for all captures (NFR-1.3).
## 2. Deliverables map
| # | Deliverable | New/Changed files |
|---|---|---|
| D1 | Analytics engine | `helpers/report_util.js` (new) |
| D2 | Endpoint + datasource builder | `controllers/advanced_report.js` (new), `routes/job.js` (route), `model/job.js` (report-contents options persistence) |
| D3 | Map page variants + multi-capture | `public/sprayMap.html` (variants), `helpers/web_util.js` (browser reuse) |
| D4 | Template + validation | `reports/app_advanced.mrt` (authored in the Stimulsoft designer), `scripts/validate_advanced_report_template.js` (new) |
| D5 | Frontend wiring | client repo: Report Settings "Report Contents" panel, advanced-report option, viewer call |
| D6 | Tests + offline harness | `tests/` fixtures + unit/integration tests, offline Stimulsoft harness |
| D7 | Rollout | backfill verification, template deploy to `REPORT_DIR`, release notes |
Sequencing: **D1 → D2 → (D3 ∥ D4) → D5 → D6 → D7.** D3 and D4 are independent once D2 fixes the datasource shape. D6 grows alongside every deliverable (D1 unit tests land with D1).
## 3. D1 — Analytics engine (`helpers/report_util.js`)
Pure functions over point arrays; no I/O.
1. **Line segmentation** — group `ApplicationDetail` points by `llnum`; `sprayStat == 3` marks line start, spray-on = `sprayStat ∈ {1, 3}` (pattern: `getSprayOnSegments`, `controllers/job.js:604`).
2. **Zone assignment** — point-in-polygon (turf) of each line's points against `job.sprayAreas`; majority wins for straddling lines (FR-5.2).
3. **Per-line stats** — start time, spray time, length (`geoUtil.distance()` — **km**), avg speed, area = length × swath, app rate, avg |xTrack|, turn time (gap to next line; pattern: turn-time loop `workers/job_worker.js:1486`).
4. **Zone roll-ups** — sprayed area, coverage %, volume, flight/spray time, avg turn time, avg height, avg XT, avg flow rate (mean `lminApp` — completely new calculation, degrade when flat 0).
5. **Mission totals** — sums/weighted averages of zone values (must equal page-1 figures exactly, NFR-3.3); ferry time/distance = flight spray.
6. **Planned areas** — turf area from polygon geometry (`sprayAreas[].properties.area` absent in live data); coverage capped at 100.0% display.
7. **Weather** — reuse `jobUtil.getDataWeatherInfo(fileIds)` (`helpers/job_util.js:325`); manual `job.weatherInfo` override.
Unit-test fixtures (with D1): typical multi-zone job, no-flow-controller job (`lminApp` = 0), SatLoc-style job (no xTrack/turn data), unsprayed zone, single-zone job, boundary-straddling line.
## 4. D2 — Endpoint + datasource (`controllers/advanced_report.js`)
1. `POST /preAdvancedReport` in `routes/job.js`, same auth middleware as `preAppReport` (NFR-4.1). Returns `{ rid, path, c }` (FR-1.1).
2. Controller flow (mirrors `preAppReport_post`): load job with populated refs → persist Report Settings incl. Report Contents selections (`job.rptOp` pattern, FR-7.5) → stream `ApplicationDetail` by the job's `fileId`s with field projection (NFR-1.2) → run D1 engine → render maps via D3 → write `rptDS.json` → select template.
3. Datasource shape (all display values pre-localized strings, FR-1.3):
- `mission` — header/info block, KPI tiles, statistics, generation date.
- `zones[]` — per-zone info + stats + map image refs; empty-state zones carry `` placeholder values (FR-4.6); filtered per Report Contents options.
- `lines[]` nested per zone — flight-line table rows; single `` placeholder row for unsprayed zones; omitted when Flight Line Statistics is off.
- `products[]`, `weather` (suppressed when unavailable), `coverageCards[]` (all zones, always).
4. Template selection: `app_advanced_<applicatorId>.mrt` else `app_advanced.mrt`; applicator id sanitized to hex ObjectId (NFR-4.2).
5. Structure the generation function so a worker can call it without the HTTP layer (NFR-2.3); a simple in-process counter limits it to max 2 concurrent generations (NFR-2.2).
6. Per-phase pino logging: query, data aggregation, each capture, datasource write (NFR-7.1).
## 5. D3 — Maps (`public/sprayMap.html` variants + `helpers/web_util.js`)
1. Extend `web_util` to open one Chromium instance and capture multiple pages/states per report (NFR-1.3).
2. Mission map variant: fitBounds over all zones; numbered markers + zone/field names + acreage labels; spray/ferry layers; legend/scale/north arrow. **Mode switch** (FR-2.3.3): compute zone pixel footprint at fitted zoom — below threshold (~25 px) render locator badges (`divIcon`, zone numbers) instead of polygons (FR-2.3.2).
3. Zone detail variant: per-zone fitBounds, boundary + spray lines + dashed ferry lines, neighbouring zones faded into the background; unsprayed zones render boundary + ferry only.
4. Background toggle: when *Hide Map Background* is selected (FR-7.4), all variants skip the satellite tile layer and render on the plain dark-green background used in the mockups (a fixed CSS background on the map container) — no tile downloads during capture.
4. Thumbnails: crop from the single all-zones capture when single-viewport; per-zone captures when dispersed; **skipped entirely above 12 zones** (FR-3.5) or when zone pages are excluded (Report Contents).
5. Zone capture failure → placeholder image + log, report continues; mission map failure → request fails (NFR-3.1).
## 6. D4 — Template (`reports/app_advanced.mrt`, authored in the Stimulsoft designer)
1. Three page designs authored manually in the embedded Stimulsoft designer: Mission Overview, Mission Coverage (grid ≤12 zones / compact table >12), Zone Detail (master band per zone, flight-line table as StiPanel-wrapped child band).
2. Validation script `scripts/validate_advanced_report_template.js` (NFR-5.2/6.1), run after every designer save and before deploy. Checks: no empty `{}` collections in the `.mrt` JSON; `GlobalizationStrings` for en-US / pt-PT / es-ES with non-empty Items targeting existing components; unique component names; every band's `DataSourceName` / `MasterComponent` / `DataRelationName` resolves against the Dictionary; DataBands nested inside DataBands are StiPanel-wrapped; every `{table.column}` expression references a declared Dictionary column.
3. Section suppression via empty datasets; dash placeholder rows come from the datasource, not template logic.
4. Committed `.mrt` is the source of truth (NFR-6.1). Footer `Created <date>` + `page/totalPages`; header band per page type.
## 7. D5 — Frontend (client repo)
1. Report Settings dialog: add right-side **Report Contents** panel — Include All Zone Detail (default on), nested Sprayed Zones Only (default off), Include Flight Line Statistics (default on), Hide Map Background (default off), info tooltips (FR-7.4); restore last selections per job.
2. Advanced Report as a report option alongside the legacy report; on Preview call `preAdvancedReport` and hand `{rid, path}` to the existing viewer unchanged.
## 8. D6 — Testing & verification
1. D1 unit tests over fixtures (all FR-8 degradation rows covered, NFR-3.4).
2. Cross-page consistency test: mission totals ≡ zone roll-ups (NFR-3.3).
3. Offline Stimulsoft harness (file:// + `stimulsoft.reports.pack.js`) loading the real `.mrt` + generated `rptDS.json` — reproduces viewer load/localize/render without the app (NFR-6.2); Trial watermark acceptable in tests.
4. Integration run against a live-like multi-zone job; visual check of all three page types, both map modes, >12-zone compact layout, both unit systems, all three cultures.
5. Performance measurement against NFR-1.1 (~35 s per 10 zones; ~15 s typical 3-zone job) with per-phase timings from NFR-7.1 logs.
## 9. D7 — Rollout
1. Verify production aggregate coverage (`avgXtError`, `avgSpraySpeed`, `totalFlightLength`) on recent Applications; re-run `scripts/migrate_applications.js` only if gaps found (NFR-8.1).
2. Deploy `app_advanced*.mrt` to the environment's `REPORT_DIR` (may be outside this repo — NFR-6.4).
3. Release notes: flow-rate fields require a flow controller; SatLoc-sourced applications omit XT/turn statistics (NFR-8.2).
## 10. Open items
- **F-OQ-1 Page orientation** (portrait-only vs landscape variant) — blocks D4 template freeze; portrait assumed until the PO decides.
- **F-OQ-2 Compact coverage layout threshold** (more than 12 vs 12-and-above; threshold value) — affects D3 thumbnail logic and the D4 coverage page; "more than 12" assumed until the PO decides.
- **Regeneration reuse/caching** for repeat downloads of unchanged reports — not in Phase 1 scope; candidate optimization if NFR-1.1 budgets prove tight in practice (D2's worker-ready structure keeps the door open).