- JavaScript 91.3%
- Python 6.9%
- HTML 1.3%
- CSS 0.5%
|
|
||
|---|---|---|
| .forgejo/workflows | ||
| data | ||
| img | ||
| scripts | ||
| app.js | ||
| demographic-correlations.csv | ||
| demographic-estimated-counts.csv | ||
| demographic-segmentation.csv | ||
| demographic-vote-weighted.csv | ||
| denver-districts.geojson | ||
| denver-precincts-2023.geojson | ||
| denver-tracts-compact.geojson | ||
| denver-tracts.geojson | ||
| district-data.js | ||
| district-data.json | ||
| districts.js | ||
| ecological-inference-results.csv | ||
| favicon.svg | ||
| goodman-regression.csv | ||
| index.html | ||
| neighborhood-census-rollup.csv | ||
| neighborhood-data.js | ||
| neighborhood-rollup-2026.csv | ||
| neighborhood-turnout-comparison.csv | ||
| neighborhood-vote-decomposition.csv | ||
| neighborhoods.js | ||
| pika.png | ||
| precinct-bisg-race.csv | ||
| precinct-results.json | ||
| precinct-turnout-by-race.csv | ||
| precinct-voter-demos.csv | ||
| precincts-geo.js | ||
| README.md | ||
| style.css | ||
| three-model-comparison.csv | ||
| tracts.js | ||
Denver 2027 Election Cycle Visualizer
Interactive map and dashboard for the Denver DSA Electoral Working Group. Tracks all 13 City Council seats on the April 2027 ballot plus the November 2026 At-Large special election (Parady vacancy).
Quick start
Open index.html in a browser (no build step, no server needed — it's a static page
that loads data from local JSON files). To mirror the actual deployment (static
files served from the repo root) instead:
uv run scripts/serve.py # http://localhost:8080/
Tabs
- Dashboard — voter landscape, priority targets (top 6 by score), district summary table
- Map — 178 census tracts on OpenStreetMap with 15 toggleable data overlays
- Special Election — Parady vacancy timeline, FEF rules, 2023 at-large results, candidate tracker
Updating data
Campaign numbers (DDSA members, Melat voter %, Melat doors canvassed)
Edit district-data.json. Fields you'll most likely change:
"D6": {
"members": 120, // DDSA members in this district (est.)
"melatPct": 34, // Melat Kiros voter % in this district (est.)
"melatDoors": 4500, // doors canvassed for Melat in this district (est.)
"rating": "Open", // Safe | Vulnerable | Open
"open": "YES" // YES | No | Maybe
}
Save the file, run uv run scripts/sync-district-data.py (the page actually
loads district-data.js, a generated wrapper, so it works over file://),
and refresh. Scores, priority ordering, table data, and map overlays all
update automatically. No code changes needed.
Incumbency / candidate changes
Edit the same district-data.json. Update "incumbent", "rating", "open", and
"notes" as the field develops.
Census data (renter %, demographics, median income)
Census data is per-tract and embedded in tracts.js. This is essentially static
(ACS 2023 5-year). If you need to rebuild it from a new census spreadsheet:
uv run scripts/build-tracts.py
Voter registration / turnout
Currently at the district level in district-data.json ("voters", "turnout",
"johnston"). For precinct-level updates, source files live in
~/Downloads/Data/Voter-Registration/.
Data pipeline
All generated data files are rebuilt by scripts in scripts/ (self-contained
uv scripts; run with uv run scripts/<name>.py).
Each script documents its sources in its docstring. Network fetches are cached:
downloaded GeoJSON is reused unless you pass --fetch.
In dependency order:
| Script | Inputs | Outputs |
|---|---|---|
build-districts.py |
Denver ODC ArcGIS (2023 council districts) | denver-districts.geojson, districts.js |
build-tracts.py |
TIGERweb tracts, denver-districts.geojson, census xlsx |
denver-tracts.geojson, tracts.js |
build-precinct-results.py |
Map.xlsx (2023 mayor), Map_ENR_2026_PE.csv_data.csv (2026 CO-1) |
precinct-results.json |
build-precincts-geo.py |
Denver ODC ArcGIS (2023 precincts), precinct-results.json, precinct-now.json (2026 turnout) |
denver-precincts-2023.geojson, precincts-geo.js |
calibrate-melat-pct.py |
precincts-geo.js, official CO-1 totals |
district-data.json melatPct/melatCI, district-data.js |
sync-district-data.py |
district-data.json |
district-data.js |
process-precinct-data.py |
2020/2022/2024 primary xlsx exports | historical merge (exploratory; output not shipped) |
Scoring formula
The Target Score (1–10) weights each district by strategic priority:
| Factor | Weight | Rationale |
|---|---|---|
| Incumbency (Open=10, Vuln=6, Safe=2) | 30% | Open seats are the biggest opportunity |
| Renter % | 20% | Renters skew progressive, more reachable |
| DDSA Members | 20% | Organizing capacity on the ground |
| Melat Voter % | 15% | Proxy for left-receptive electorate |
| Johnston % (inverted) | 15% | Lower establishment pull = better opening |
Each factor is normalized 0–10, then combined with the weights above.
Source data
Raw source data is archived in the shared Google Drive folder: https://drive.google.com/drive/folders/1GC3COAuWL5P7eSEC_dlOjwUrpzFt87Uj
The pipeline scripts read from a local download of that folder (defaults point
at ~/Downloads/; override with each script's CLI flags):
Census/— census-tracts-Denver.xlsx (ACS 2023 5-year demographics)Voter-Registration/— sos-precinct-counts.xlsx, AprilStatistics2026.xlsxElection-Results/— denver-2023-results.pdf, Map.xlsx (precinct-level mayoral leaders)- Map_ENR_2026_PE.csv_data.csv (2026 CO-1 primary ENR export)
- precinct-now.json (2026 primary turnout, Power BI capture)
- ballot-returns-*.xlsx / votes-*.xlsx (2020/2022/2024 primaries)
Files
| File | Purpose |
|---|---|
index.html |
Application markup (DSA-branded, tabbed dashboard) |
style.css |
Application styles |
app.js |
Application logic (scoring, Leaflet map, overlays, tabs) |
district-data.json |
Edit this — district-level campaign data (then run sync-district-data.py) |
district-data.js |
Generated from district-data.json; loaded by the page |
tracts.js |
Tract GeoJSON + census demographics (178 Denver tracts) |
districts.js |
Council district boundary overlay |
precincts-geo.js |
Precinct polygons + 2026 CO-1 results + 2026 turnout |
precinct-results.json |
Per-precinct 2026 CO-1 + 2023 mayor leaders |
denver-*.geojson |
Cached source geometry (see Data pipeline) |
scripts/ |
Rebuild pipeline (uv scripts) |
Design
Denver DSA brand: Manifold DSA font, #EC1F27 (red) / #231F20 (black) palette.
Reference: ~/dev/ddsa/proposal1/ (Proposal 1: Bold Typography).