Skip to content

meub/pps-closures

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PPS Closures

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.

Live site

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

What's in the dashboard

  • 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.

Data sources

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

Project structure

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)

Reproducing the pipeline

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.py

Each step is idempotent; rerun freely as sources refresh.

Spatial-join methodology

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.

Caveats

  • 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_2022 counts 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.py filters them out.

Excluded from this repository

  • .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.

Credits

Built by Alex Meub with help from Claude Code.

About

PPS Closures is a visualization of data from Portland Public schools that may factor in to closure decisions

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors