105 lines
9.6 KiB
Markdown
105 lines
9.6 KiB
Markdown
# 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).
|