# app/models/green_space.py from pydantic import BaseModel, Field from typing import Optional, List, Dict, Tuple, Any from datetime import datetime from enum import Enum class GreenSpaceType(str, Enum): PARK = "park" GARDEN = "garden" FOREST = "forest" RIVERSIDE = "riverside" CEMETERY = "cemetery" PLAYGROUND = "playground" SPORTS_FIELD = "sports_field" ALLOTMENT = "allotment" SQUARE = "square" OTHER = "other" class NoiseLevel(int, Enum): VERY_QUIET = 1 QUIET = 2 MODERATE = 3 NOISY = 4 VERY_NOISY = 5 class AmenityType(str, Enum): TOILET = "toilet" RESTAURANT = "restaurant" CAFE = "cafe" ICE_CREAM = "ice_cream" SPATI = "spati" PLAYGROUND = "playground" WATER_FEATURE = "water_feature" FITNESS = "fitness" BIKE_RENTAL = "bike_rental" PARKING = "parking" PUBLIC_TRANSPORT = "public_transport" class Coordinates(BaseModel): lat: float = Field(..., ge=-90, le=90) lng: float = Field(..., ge=-180, le=180) class Amenity(BaseModel): id: str name: str type: AmenityType coordinates: Coordinates distance_meters: int rating: Optional[float] = Field(None, ge=0, le=5) opening_hours: Optional[str] = None description: Optional[str] = None class EnvironmentalFeatures(BaseModel): """Environmental characteristics of the green space.""" tree_coverage_percent: int = Field(..., ge=0, le=100, description="Percentage of area covered by trees") shade_quality: int = Field(..., ge=0, le=100, description="Quality of shade availability") noise_level: NoiseLevel = Field(..., description="Average noise level") wildlife_diversity_score: int = Field(..., ge=0, le=100, description="Biodiversity indicator") water_features: bool = Field(False, description="Has ponds, fountains, or streams") natural_surface_percent: int = Field(..., ge=0, le=100, description="Percentage of natural vs paved surface") class AccessibilityFeatures(BaseModel): """Accessibility and infrastructure features.""" wheelchair_accessible: bool = Field(False) public_transport_score: int = Field(..., ge=1, le=5, description="Public transport accessibility") cycling_infrastructure: bool = Field(False, description="Has bike paths or bike parking") parking_availability: int = Field(..., ge=1, le=5, description="Car parking availability") lighting_quality: int = Field(..., ge=1, le=5, description="Evening lighting quality") class RecreationFeatures(BaseModel): """Recreation and activity features.""" playground_quality: int = Field(0, ge=0, le=100, description="Playground facilities quality") sports_facilities: bool = Field(False, description="Has sports courts, fitness equipment") running_paths: bool = Field(False, description="Has designated running/walking paths") cycling_paths: bool = Field(False, description="Has cycling paths") dog_friendly: bool = Field(True, description="Dogs allowed") bbq_allowed: bool = Field(False, description="BBQ/grilling allowed") class PersonalityScore(BaseModel): """Personality-specific score with explanation.""" personality: str score: int = Field(..., ge=0, le=100) explanation: str key_factors: List[str] recommendations: List[str] class GreenSpace(BaseModel): """Complete green space model with all features and scoring.""" # Basic info id: str name: str description: Optional[str] = None type: GreenSpaceType # Location coordinates: Coordinates neighborhood: str address: Optional[str] = None # Physical characteristics area_sqm: Optional[int] = Field(None, ge=1, description="Area in square meters") perimeter_m: Optional[int] = Field(None, ge=1, description="Perimeter in meters") # Features environmental: EnvironmentalFeatures accessibility: AccessibilityFeatures recreation: RecreationFeatures # Nearby amenities (within search radius) nearby_amenities: List[Amenity] = [] # Personality scoring (for current request) current_personality_score: Optional[PersonalityScore] = None all_personality_scores: Optional[Dict[str, int]] = None # Metadata last_updated: datetime data_sources: List[str] = [] confidence_score: int = Field(..., ge=0, le=100, description="Data quality confidence") class ScoringRequest(BaseModel): """Request parameters for green space scoring.""" personality: str location: Optional[Tuple[float, float]] = None # (lat, lng) radius: int = Field(2000, ge=100, le=10000) neighborhood: Optional[str] = None min_score: int = Field(60, ge=0, le=100) filters: Dict[str, Any] = {} limit: int = Field(20, ge=1, le=100) include_amenities: bool = False class LocationScore(BaseModel): """Score for a specific location within a green space.""" coordinates: Coordinates score: int = Field(..., ge=0, le=100) explanation: str nearby_features: List[str] best_for: List[str] # Best personality types for this location recommendations: List[str]