Indy Explorer helps you navigate resorts in the Indy Pass with ease — filter by region, blackout dates, reservation requirements, Peak Rankings scores, and more.
Live app: indy-explorer.vercel.app
/
├── backend/ # FastAPI app (Fly.io)
├── frontend/ # Vite + React app (Vercel)
├── pipeline/ # Data pipeline (scraping, geocoding, merging)
│ ├── pipeline.py # Pipeline orchestrator
│ ├── page_scraper.py
│ ├── blackout.py
│ ├── reservations.py
│ ├── prep_resort_data.py
│ └── utils.py (+ others)
├── legacy/ # Legacy Streamlit redirect page (points to Vercel app)
├── data/ # Committed CSV data consumed by the backend
└── tests/ # Test suite
The pipeline in pipeline/ feeds data/resorts.csv, which is committed to the repo and read at startup by the FastAPI backend.
- Resort Data Extraction: Scrapes resort pages from indyskipass.com with BeautifulSoup.
- Location Normalization: Google Maps Geocoding API normalizes city, state, and country.
- Blackout Date Filtering: Filter by blackout dates for both standard Indy Pass and Learn To Turn (LTT).
- LTT Pass Support: Filter for resorts in the Learn To Turn program with their own blackout calendar.
- Peak Rankings Integration: Resort quality scores from peakrankings.com.
- Interactive Map: deck.gl/Mapbox map with clickable resort dots and auto-zoom.
| Source | What it provides |
|---|---|
| indyskipass.com | Resort details, stats, coordinates, blackout dates, reservation requirements |
| Indy Pass LTT Google Sheet | Learn To Turn participating resorts and blackout dates |
| peakrankings.com | Resort quality scores and rankings |
| Google Maps Geocoding API | Normalized city, state, and country |
The pipeline orchestrator handles scraping, geocoding, blackout dates, rankings, and merging.
The pipeline automatically backs up data/resorts.csv, data/resort_id_map.csv, and data/pipeline_metadata.json to a timestamped directory under backups/ before any steps run. The 10 most recent backups are kept; older ones are deleted. To restore, copy the desired files back to data/.
# Incremental refresh (uses cached HTML, fetches new resorts only)
poetry run python pipeline/pipeline.py
# Full refresh (re-scrape all pages, regenerate geocoded locations)
poetry run python pipeline/pipeline.py --full
# Run specific steps only
poetry run python pipeline/pipeline.py --steps fetch_peak_rankings,prepAvailable pipeline steps (run in order):
| Step | Description |
|---|---|
scrape_resorts |
Scrape Indy Pass resort pages |
scrape_reservations |
Scrape reservation requirements |
fetch_blackout_dates |
Fetch blackout dates from Google Sheets |
fetch_ltt_dates |
Fetch LTT blackout dates from Google Sheets |
fetch_peak_rankings |
Fetch Peak Rankings from Google Sheets |
geocode |
Geocode resort locations via Google Maps |
prep |
Merge all data into data/resorts.csv |
| Variable | Location | Required for |
|---|---|---|
GOOGLE_MAPS_API_KEY |
.env |
Geocoding (only when regenerating data/resort_locations.csv) |
VITE_MAPBOX_TOKEN |
Vercel dashboard | Map rendering in the React frontend |
poetry run pytest # run the full test suite
poetry run black --check . # check formatting
poetry run black . # fix formattingGitHub Actions runs tests and Black checks on push/PRs. Coverage is optionally uploaded to Codecov.
- Frontend: indy-explorer.vercel.app (Vercel, auto-deploys from
main) - Backend: indy-explorer-backend.fly.dev (Fly.io, deploy with
flyctl deploy --config backend/fly.toml)
- Fork the repository.
- Create a new branch (
git checkout -b feature/my-change). - Make your changes and run tests.
- Open a Pull Request.
GNU General Public License v3.0. See LICENSE for details.
Made by Jonathan Stelman