Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ def create_snowfall_barplot(snowfall_avg, snowfall_max):


# Load resort data
resorts = pd.read_csv('data/resorts.csv', index_col='index', na_values=[''], keep_default_na=False)
resorts = pd.read_csv('data/resorts.csv', na_values=[''], keep_default_na=False)

# Drop resorts that don't have coordinate data (cannot be mapped)
resorts = resorts[resorts.latitude.notnull()]
Expand Down
11 changes: 11 additions & 0 deletions backend/data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import os
import pandas as pd
from models import Resort

RESORTS_CSV = os.path.join(os.path.dirname(__file__), '..', 'data', 'resorts.csv')


def load_resorts() -> list[Resort]:
df = pd.read_csv(RESORTS_CSV, na_values=[''], keep_default_na=False)
df = df.where(pd.notna(df), other=None)
return [Resort(**row) for row in df.to_dict(orient='records')]
81 changes: 81 additions & 0 deletions backend/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
from typing import Optional
from pydantic import BaseModel


class Resort(BaseModel):
resort_id: str
name: str
location_name: Optional[str] = None
description: Optional[str] = None
region: str
city: Optional[str] = None
state: Optional[str] = None
country: Optional[str] = None
indy_page: str
website: Optional[str] = None
reservation_status: str
reservation_url: Optional[str] = None
latitude: Optional[float] = None
longitude: Optional[float] = None
vertical: Optional[float] = None
vertical_meters: Optional[float] = None
has_alpine: Optional[bool] = None
has_cross_country: Optional[bool] = None
is_allied: Optional[bool] = None
acres: Optional[float] = None
num_trails: Optional[float] = None
trail_length_mi: Optional[float] = None
trail_length_km: Optional[float] = None
num_lifts: Optional[float] = None
vertical_base_ft: Optional[float] = None
vertical_summit_ft: Optional[float] = None
vertical_elevation_ft: Optional[float] = None
has_night_skiing: Optional[bool] = None
has_terrain_parks: Optional[bool] = None
is_dog_friendly: Optional[bool] = None
has_snowshoeing: Optional[bool] = None
difficulty_beginner: Optional[float] = None
difficulty_intermediate: Optional[float] = None
difficulty_advanced: Optional[float] = None
snowfall_average_in: Optional[float] = None
snowfall_high_in: Optional[float] = None
has_alpine_display: Optional[str] = None
has_cross_country_display: Optional[str] = None
is_dog_friendly_display: Optional[str] = None
has_night_skiing_display: Optional[str] = None
has_terrain_parks_display: Optional[str] = None
is_allied_display: Optional[str] = None
location_name_tt: Optional[str] = None
acres_tt: Optional[str] = None
vertical_tt: Optional[str] = None
num_trails_tt: Optional[str] = None
num_lifts_tt: Optional[str] = None
blackout_named_ranges: Optional[str] = None
blackout_additional_dates: Optional[str] = None
blackout_all_dates: Optional[str] = None
blackout_count: Optional[int] = None
ltt_available: Optional[bool] = None
ltt_blackout_all_dates: Optional[str] = None
ltt_blackout_count: Optional[int] = None
pr_snow: Optional[float] = None
pr_resiliency: Optional[float] = None
pr_size: Optional[float] = None
pr_terrain_diversity: Optional[float] = None
pr_challenge: Optional[float] = None
pr_lifts: Optional[float] = None
pr_crowd_flow: Optional[float] = None
pr_facilities: Optional[float] = None
pr_navigation: Optional[float] = None
pr_mountain_aesthetic: Optional[float] = None
pr_total: Optional[float] = None
pr_overall_rank: Optional[float] = None
pr_regional_rank: Optional[float] = None
pr_region: Optional[str] = None
pr_lodging: Optional[str] = None
pr_apres_ski: Optional[str] = None
pr_access_road: Optional[str] = None
pr_ability_low: Optional[str] = None
pr_ability_high: Optional[str] = None
pr_nearest_cities: Optional[str] = None
pr_pass_affiliation: Optional[str] = None
pr_total_tt: Optional[str] = None
1 change: 1 addition & 0 deletions backend/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
fastapi==0.115.12
uvicorn[standard]==0.34.0
pandas==2.2.3
pydantic==2.12.5

# dev/test
httpx==0.28.1
Expand Down
29 changes: 29 additions & 0 deletions backend/tests/test_resort_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import sys
import os

sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))

from data import load_resorts
from models import Resort


def test_load_resorts_returns_list_of_resort():
resorts = load_resorts()
assert len(resorts) > 0
assert all(isinstance(r, Resort) for r in resorts)


def test_all_resorts_have_required_fields():
resorts = load_resorts()
for resort in resorts:
assert resort.resort_id
assert resort.name
assert resort.region
assert resort.indy_page
assert resort.reservation_status


def test_resort_ids_are_unique():
resorts = load_resorts()
ids = [r.resort_id for r in resorts]
assert len(ids) == len(set(ids))
Loading
Loading