fix(compare): a no-results school no longer blanks the trend chart #80

Merged
tudor merged 1 commits from fix/compare-null-year-blanks-chart into main 2026-07-22 14:23:25 +00:00
Owner

The bug

On the Compare page, adding a school with no results data made every school's trend line vanish from the "Explore trends" chart — the schools only reappeared once that school was removed.

Root cause

A school with no performance rows still comes back from /api/compare with a single phantom yearly_data row (produced by the dim_school LEFT JOIN marts.fact_performance in data_loader.py) where year and every metric are null.

In buildCompareChart, Math.trunc(null) is 0, so the year axis was seeded at year 0. fillAcademicYears then walked 0, 101, 202, … and hit its 50-iteration safety cap long before reaching the real academic years (e.g. 202223). Result: the axis was 50 garbage years and every school's series mapped entirely to null — a blank chart.

The sections above the chart were unaffected (they read latest values null-safely), which is why only the trend lines disappeared.

Fix

Ignore yearly rows without a real numeric year when building both the axis (rawYears) and the per-school year map. A null/NaN year is not a data point.

Test

Added a regression test in compareChartData.test.ts: a no-results school (single year: null row) alongside three real schools must not pollute the axis — the real years drive the chart and all three real lines still render. Full suite (113 tests) and typecheck pass.

Note (not fixed here)

The deeper source is the backend emitting a phantom year: null row for no-results schools. Every current consumer guards against it except the chart, so this fix is localised to the chart. Worth a follow-up to drop that phantom row in the compare endpoint if we want defence in depth.

🤖 Generated with Claude Code

## The bug On the Compare page, adding a school with **no results data** made every school's trend line vanish from the "Explore trends" chart — the schools only reappeared once that school was removed. ## Root cause A school with no performance rows still comes back from `/api/compare` with a single **phantom `yearly_data` row** (produced by the `dim_school LEFT JOIN marts.fact_performance` in `data_loader.py`) where `year` and every metric are `null`. In `buildCompareChart`, `Math.trunc(null)` is `0`, so the year axis was seeded at year 0. `fillAcademicYears` then walked `0, 101, 202, …` and hit its 50-iteration safety cap **long before reaching the real academic years** (e.g. `202223`). Result: the axis was 50 garbage years and every school's series mapped entirely to `null` — a blank chart. The sections above the chart were unaffected (they read latest values null-safely), which is why only the trend lines disappeared. ## Fix Ignore yearly rows without a real numeric year when building both the axis (`rawYears`) and the per-school year map. A null/`NaN` year is not a data point. ## Test Added a regression test in `compareChartData.test.ts`: a no-results school (single `year: null` row) alongside three real schools must not pollute the axis — the real years drive the chart and all three real lines still render. Full suite (113 tests) and typecheck pass. ## Note (not fixed here) The deeper source is the backend emitting a phantom `year: null` row for no-results schools. Every current consumer guards against it except the chart, so this fix is localised to the chart. Worth a follow-up to drop that phantom row in the compare endpoint if we want defence in depth. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
tudor added 1 commit 2026-07-22 14:18:01 +00:00
fix(compare): a no-results school no longer blanks the trend chart
PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m8s
PR Checks / Backend Smoke (pull_request) Successful in 7s
PR Checks / Build Backend (no push) (pull_request) Successful in 13s
PR Checks / Build Frontend (no push) (pull_request) Successful in 49s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 10s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 54s
dc85254ad2
Adding a school with no performance data to a comparison made every
school's trend line disappear until that school was removed.

Root cause: /api/compare returns such a school with a single phantom
yearly_data row (the dim_school LEFT JOIN) whose year is null. In
buildCompareChart, Math.trunc(null) is 0, so the axis was seeded at
year 0; fillAcademicYears then walked 0, 101, 202, … and hit its
50-step cap long before reaching the real years, leaving every
school's series mapped entirely to null.

Fix: ignore yearly rows without a real numeric year when building the
axis and the per-school year map. Regression test added.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

🤖 AI Code Review (Claude Code)

This PR fixes a bug where a school with no performance data returns a single phantom yearly_data row (year=null from the LEFT JOIN), which previously got coerced to year 0 via Math.trunc(null) and blew out the chart's axis range, blanking every school's line. The fix adds a hasYear() guard filtering out non-finite/non-number years before axis computation and per-school data mapping, with a matching regression test.

No issues found.

## 🤖 AI Code Review (Claude Code) This PR fixes a bug where a school with no performance data returns a single phantom yearly_data row (year=null from the LEFT JOIN), which previously got coerced to year 0 via Math.trunc(null) and blew out the chart's axis range, blanking every school's line. The fix adds a hasYear() guard filtering out non-finite/non-number years before axis computation and per-school data mapping, with a matching regression test. ✅ No issues found.
tudor merged commit 598ba9af4e into main 2026-07-22 14:23:25 +00:00
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: tudor/school_compare#80