A data-driven explorer for the 74 Portland Public Schools (PPS) elementary, K-8, middle, and alternative schools that the district plans to narrow to a 5–10 school closure shortlist in November 2026.
The dashboard gathers enrollment, building, demographic, academic, and neighborhood-housing data for every in-scope school, with an emphasis on the 15 lowest-enrollment schools that Willamette Week listed as potential candidates. PPS has not released its own shortlist.
web/index.html is a single-page static dashboard (vanilla JS + Plotly.js + Leaflet). It reads from web/data.json, which is generated by the pipeline below.
Serve locally:
python3 -m http.server -d web 8000
# open http://localhost:8000- Map of every in-scope school, color-coded by potential-candidate status.
- Ranking charts for the 15 potential candidates (enrollment, crowding, URM seismic risk, pipeline family units, recent permits).
- Scatter plots comparing enrollment to crowding, math proficiency to poverty, enrollment change to nearby residential permits, and year-built to enrollment.
- Sortable table of all 74 schools with column descriptions and source citations.
- Methodology section documenting every source with vintage.
| Source | What it provides | Vintage |
|---|---|---|
| Oregon ODE Fall Membership | Enrollment by school (2024-25, 2025-26), race/ethnicity | 2025-26 |
| Oregon OSAS (PAGR) | ELA & math state-test proficiency | 2023-24, 2024-25 |
| NCES Common Core of Data | Free/reduced lunch counts, geocoded addresses | 2022-23 |
| US Dept of Ed CRDC | English learners, IDEA/SPED, chronic absenteeism | 2020 (COVID year, likely suppressed) |
| KPFF Seismic Report 2009 + Holmes Engineering 2024 | Year built, square footage, construction type, URM retrofit estimates | 2009 / 2024 |
| PPS Bond page | Seismic retrofit status | 2025 |
| Oregon Affordable Housing Inventory (OAHI) | Existing + in-development subsidized housing | 2024 |
| Metro RLIS | Supplementary regional housing | 2024 |
| Portland BDS / PortlandMaps | Residential building permits (2022+) | 2022 – present |
| City of Portland — School Boundaries | PPS attendance polygons (ArcGIS FeatureServer) | 2024-25 |
| Willamette Week 2026-03-18 | List of 15 lowest-enrollment schools (enrollment-based ranking) | 2026-03-18 |
| Metro 2045 Distributed Forecast (Ord. 21-1457) | Regional growth context (city/county only) | 2021 |
data/
pps_schools.csv master table (83 rows × 54 cols; 74 in-scope + 9 high schools)
raw/ source CSVs, JSONs, XLSXs, PDFs, GeoJSONs
scripts/
fetch_bds_permits.py → data/raw/portland_bds_permits.csv
fetch_affordable_pipeline.py → data/raw/portland_affordable_pipeline.csv
fetch_boundaries.py → data/raw/pps_boundaries_{level}.geojson
boundary_join.py shared BoundaryIndex (point-in-polygon w/ haversine fallback)
build_master.py assemble data/pps_schools.csv
merge_housing.py + affordable_units / pipeline_* columns
merge_permits.py + permit columns
export_web.py → web/data.json (filters to the 74 in-scope schools)
web/
index.html single-page dashboard
data.json dashboard payload (generated)
Requires Python 3.11+ and the packages in pandas, requests, openpyxl, shapely.
python3 -m venv .venv
source .venv/bin/activate
pip install pandas requests openpyxl shapely
# 1. Fetch raw sources (network):
python scripts/fetch_bds_permits.py
python scripts/fetch_affordable_pipeline.py
python scripts/fetch_boundaries.py
# 2. Build master CSV:
python scripts/build_master.py
# 3. Spatial joins (housing + permits into the master):
python scripts/merge_housing.py
python scripts/merge_permits.py
# 4. Export dashboard payload:
python scripts/export_web.pyEach step is idempotent; rerun freely as sources refresh.
Every "nearby" metric (affordable units, pipeline family units, residential permits) is aggregated over the school's PPS attendance polygon rather than a 1-mile circle. Schools without a published catchment (ACCESS Academy, focus-option programs, some alternatives) fall back to a 1-mile haversine radius. boundary_join.BoundaryIndex handles the lookup and the name-alias table between ODE and the City's boundary feed.
- Potential closure list is unofficial. The 15-school list is Willamette Week's enrollment-only ranking; PPS has not published a shortlist. The district is expected to name one in November 2026.
- Academic performance is included for context but is notably absent from the criteria PPS has discussed publicly.
- CRDC (LEP, SPED, chronic absenteeism) is from 2020 — a COVID-suppressed reporting year. Percentages are CRDC counts ÷ current enrollment.
- Square footage is from a 2009 inventory and may predate bond-funded expansions.
- Building permits ≠ completions.
permits_units_within_1mi_since_2022counts approved units from 2022-01-01 forward, not occupied units. - High schools are out of scope. PPS's announced closure process covers elementary / K-8 / middle / alternative only. The master CSV keeps high schools for reference but
export_web.pyfilters them out.
.venv/— Python virtualenv, regenerate locally.data/raw/LRFP_Vol2_2021.pdf(134 MB) — PPS Long-Range Facility Plan Vol. 2, available on bond.pps.net.data/raw/metro/(336 MB) — Metro 2045 forecast .ppkx + extracted GDB, available on oregonmetro.gov.
Built by Alex Meub with help from Claude Code.