We would like this project to be considered for Track 1 ("Campus Impact") and Track 2 ("Usability & Craft").
GitHub repository: https://github.com/bene01-git/ezpark
Introducing EZPark!
Anyone who has driven to Syracuse University knows the ritual: circling College Place, squinting at a permit sign, trying to remember whether Orange lots open at 4:30 or 5:00, and wondering if the Dome has a game that just swallowed every spot near campus. The rules are real, but they live scattered across PDFs, permit pages, and word of mouth. We didn't want another static map — we wanted a single, honest answer to one question: can I park here, right now, given who I am?
The idea grew from there. If we were already answering that for campus lots, why not the city and private garages downtown too? And the question everyone actually asks — is it full? — became the north star, even though that turned out to be the hardest part.
What It Does
EZPark is a web app that shows every SU lot and nearby downtown garage on a map and tells you, in real time, whether each one is open (green) or closed (red) to you. You pick your permit type — visitor, commuter, grad, faculty — and the app evaluates the actual rules against the current moment:
- Permit eligibility per lot
- The "Orange lot after 4:30 p.m. on weekdays / open on weekends" rule
- Dome/event closures
- Metered street parking and winter odd/even rules
It also folds in non-SU garages (city and private, downtown), a "Locate me" button that drops a dot on your position, and a crowdsourced "is it full?" layer where users tap Open / Some / Full and everyone sees a recent, weighted consensus.
How It's Built
The core constraint shaped everything: To save time to fit the 24-hour period, we wanted to host it entirely on GitHub Pages, which only serves static files and can't run Python when someone visits. With this in mind, we split the app into a build-time brain and a run-time face.
The rules engine (Python). All parking rules live as declarative data in a lots.json file — each lot carries its category, eligible permits, Orange-hour cutoff, and event-restriction flags. A small Python engine interprets that data to produce an open / closed verdict, with a CLI and a pytest suite. Because the rules are data rather than code, I could mirror the same interpreter in JavaScript for the browser and keep the two in sync.
The data pipeline (Python + GitHub Actions). Since Pages can't run Python live, GitHub Actions became our backend. On every push and on a daily schedule, it runs our scripts to:
- pull parking geometry from OpenStreetMap via the Overpass API,
- scrape the university's current "sold out" permit list,
- merge everything into a single static JSON bundle,
and then deploys the site. It's free, it's server-less, and it's entirely inside GitHub.
The Frontend (Leaflet + vanilla JS). A Leaflet map reads the bundle, colors each dot by open/closed, and re-evaluates every minute so statuses flip as the day moves. Geolocation uses the browser's location API (HTTPS-only, which Pages provides).
Crowdsourced fullness (Supabase). Because a static site can't store state, reports go to a free Supabase table locked down with row-level security so the public key can only insert and read. The consensus isn't a raw vote — recent reports count more. Each report $i$ of age $a_i$ minutes gets a linearly decaying weight over a window $W$:
$$w_i = \max!\left(0,\; 1 - \frac{a_i}{W}\right)$$
and the displayed status is the weighted plurality across the three options:
$$\text{status} = \operatorname*{arg\,max}{s \,\in\, {\text{open},\,\text{some},\,\text{full}}} \; \sum{i \,:\, r_i = s} w_i$$
so a "Full" from two minutes ago outweighs an "Open" from an hour ago.
Challenges We Ran Into
There is no real-time parking feed. The feature we most wanted — live open-space counts — simply doesn't exist as public data for SU or downtown Syracuse. Rather than fake it, we reframed the whole project: a rules engine answers "am I allowed to park here now," and crowdsourcing approximates "is there space." That pivot, from an impossible feed to an honest model, ended up being the best design decision we made.
Static hosting still has its flaws. No server means no hidden API keys and no server-side code, which is why GitHub Actions and Supabase's row-level security became load-bearing. Keeping the Python and JavaScript rule interpreters consistent also pushed me toward the declarative-JSON approach so there was one source of truth.
The deployment gauntlet. Getting green in CI was its own adventure: a first push rejected because we'd initialized the repo with a README, a pytest run that couldn't import our package until we set the test path config, and a wave of Node 20 deprecation warnings that meant bumping my GitHub Actions to their newer versions. Each was small; together they taught me a lot about how CI actually resolves paths and runtimes.
Data honesty. Coordinates, prices, and permit mappings are approximate and go stale, so we built the app to flag that plainly and to prefer pointing at live sources over pretending to be authoritative.
Log in or sign up for Devpost to join the conversation.