Complete Project 2: Weather App with API

This project teaches API integration, JSON data handling, error management, and creating a useful utility application. We'll create a console-based weather application that fetches current weather and forecasts from a simulated API, with features like city search, temperature unit conversion, and weather alerts.

# WEATHER APP WITH API
import json
import datetime
import sys

print("WEATHER APPLICATION")
print("=" * 50)

class WeatherApp:
    """A weather application using simulated API data"""
    
    def __init__(self):
        self.units = "metric"  # Celsius by default
        self.recent_searches = []
        self.favorite_cities = []
        self.api_key = "demo_key"  # In real app, store securely
        
        # Simulated API data for demonstration
        self.weather_data = {
            "New York": {
                "current": {
                    "temp_c": 15,
                    "temp_f": 59,
                    "condition": "Partly Cloudy",
                    "humidity": 65,
                    "wind_kph": 12,
                    "feelslike_c": 14,
                    "uv": 5,
                    "last_updated": "2024-03-15 14:30"
                },
                "forecast": [
                    {"date": "2024-03-16", "max_temp_c": 16, "min_temp_c": 8, "condition": "Sunny"},
                    {"date": "2024-03-17", "max_temp_c": 14, "min_temp_c": 7, "condition": "Rainy"},
                    {"date": "2024-03-18", "max_temp_c": 12, "min_temp_c": 5, "condition": "Cloudy"}
                ]
            },
            "London": {
                "current": {
                    "temp_c": 10,
                    "temp_f": 50,
                    "condition": "Rainy",
                    "humidity": 85,
                    "wind_kph": 18,
                    "feelslike_c": 8,
                    "uv": 2,
                    "last_updated": "2024-03-15 14:45"
                },
                "forecast": [
                    {"date": "2024-03-16", "max_temp_c": 11, "min_temp_c": 6, "condition": "Cloudy"},
                    {"date": "2024-03-17", "max_temp_c": 12, "min_temp_c": 7, "condition": "Partly Cloudy"},
                    {"date": "2024-03-18", "max_temp_c": 10, "min_temp_c": 5, "condition": "Rainy"}
                ]
            },
            "Tokyo": {
                "current": {
                    "temp_c": 18,
                    "temp_f": 64,
                    "condition": "Sunny",
                    "humidity": 55,
                    "wind_kph": 8,
                    "feelslike_c": 19,
                    "uv": 7,
                    "last_updated": "2024-03-15 15:00"
                },
                "forecast": [
                    {"date": "2024-03-16", "max_temp_c": 19, "min_temp_c": 12, "condition": "Sunny"},
                    {"date": "2024-03-17", "max_temp_c": 17, "min_temp_c": 11, "condition": "Cloudy"},
                    {"date": "2024-03-18", "max_temp_c": 16, "min_temp_c": 10, "condition": "Rainy"}
                ]
            }
        }
    
    def display_menu(self):
        """Display main menu"""
        print("\n" + "☀️" * 20)
        print("WEATHER APP MENU")
        print("☀️" * 20)
        print("1. Check Current Weather")
        print("2. Check Weather Forecast")
        print("3. Toggle Temperature Units (C/F)")
        print("4. Recent Searches")
        print("5. Manage Favorite Cities")
        print("6. Weather Alerts")
        print("7. Exit")
        print("-" * 40)
        print(f"Current units: {'°C' if self.units == 'metric' else '°F'}")
    
    def get_city_input(self):
        """Get city name from user"""
        city = input("\nEnter city name: ").strip().title()
        if not city:
            print("City name cannot be empty!")
            return None
        return city
    
    def simulate_api_call(self, city):
        """Simulate API call to get weather data"""
        # Simulate network delay
        import time
        print(f"Fetching weather data for {city}...")
        time.sleep(1)  # Simulate API delay
        
        if city in self.weather_data:
            # Add to recent searches
            if city not in self.recent_searches:
                self.recent_searches.append(city)
                if len(self.recent_searches) > 5:
                    self.recent_searches.pop(0)
            return self.weather_data[city]
        else:
            # Simulate city not found
            print(f"City '{city}' not found in our database.")
            print("Available cities: New York, London, Tokyo")
            return None
    
    def convert_temperature(self, temp_c):
        """Convert temperature based on selected units"""
        if self.units == "imperial":
            return temp_c * 9/5 + 32
        return temp_c
    
    def get_temperature_unit(self):
        """Get temperature unit symbol"""
        return "°F" if self.units == "imperial" else "°C"
    
    def display_current_weather(self, city, weather):
        """Display current weather information"""
        current = weather["current"]
        
        print("\n" + "=" * 50)
        print(f"🌤  CURRENT WEATHER IN {city.upper()}")
        print("=" * 50)
        
        # Temperature
        temp = self.convert_temperature(current["temp_c"])
        feels_like = self.convert_temperature(current["feelslike_c"])
        unit = self.get_temperature_unit()
        
        print(f"Temperature: {temp:.1f}{unit}")
        print(f"Feels like: {feels_like:.1f}{unit}")
        
        # Weather condition with emoji
        condition = current["condition"]
        condition_emoji = self.get_weather_emoji(condition)
        print(f"Condition: {condition_emoji} {condition}")
        
        # Other details
        print(f"Humidity: {current['humidity']}%")
        print(f"Wind Speed: {current['wind_kph']} km/h")
        print(f"UV Index: {current['uv']}")
        
        # UV index warning
        uv = current["uv"]
        if uv >= 8:
            print("⚠  High UV! Wear sunscreen!")
        elif uv >= 6:
            print("⚠  Moderate UV protection needed")
        
        print(f"Last Updated: {current['last_updated']}")
        print("=" * 50)
        
        # Weather advice
        self.give_weather_advice(condition, temp)
    
    def get_weather_emoji(self, condition):
        """Get emoji for weather condition"""
        emoji_map = {
            "Sunny": "☀️",
            "Partly Cloudy": "⛅",
            "Cloudy": "☁️",
            "Rainy": "🌧️",
            "Snowy": "❄️",
            "Stormy": "⛈️",
            "Windy": "💨",
            "Foggy": "🌫️"
        }
        return emoji_map.get(condition, "🌡️")
    
    def give_weather_advice(self, condition, temperature):
        """Give advice based on weather conditions"""
        print("\n💡 WEATHER ADVICE:")
        
        if "Rain" in condition:
            print("  • Don't forget your umbrella! ☔")
            print("  • Drive carefully on wet roads")
        elif temperature > 30:
            print("  • Stay hydrated! 💧")
            print("  • Avoid outdoor activities during peak heat")
        elif temperature < 5:
            print("  • Bundle up! 🧣🧤")
            print("  • Watch for icy conditions")
        elif "Sunny" in condition:
            print("  • Perfect day for outdoor activities! 🌳")
            print("  • Don't forget sunscreen! ☀️")
        elif "Windy" in condition:
            print("  • Secure loose outdoor items")
            print("  • Be cautious while driving")
        else:
            print("  • Enjoy your day! 😊")
    
    def display_forecast(self, city, weather):
        """Display weather forecast"""
        forecast = weather["forecast"]
        
        print("\n" + "=" * 50)
        print(f"📅 3-DAY FORECAST FOR {city.upper()}")
        print("=" * 50)
        
        for day in forecast:
            date_obj = datetime.datetime.strptime(day["date"], "%Y-%m-%d")
            weekday = date_obj.strftime("%a")  # Monday, Tuesday, etc
            
            max_temp = self.convert_temperature(day["max_temp_c"])
            min_temp = self.convert_temperature(day["min_temp_c"])
            unit = self.get_temperature_unit()
            
            emoji = self.get_weather_emoji(day["condition"])
            
            print(f"\n{weekday} ({day['date']}):")
            print(f"  {emoji} {day['condition']}")
            print(f"  High: {max_temp:.1f}{unit}, Low: {min_temp:.1f}{unit}")
            
            # Special notes
            if "Rain" in day["condition"]:
                print("  ☔ Rain expected")
            elif day["max_temp_c"] > 25:
                print("  🔥 Hot day ahead")
            elif day["min_temp_c"] < 0:
                print("  ❄️ Freezing temperatures overnight")
        
        print("=" * 50)
    
    def toggle_units(self):
        """Toggle between Celsius and Fahrenheit"""
        if self.units == "metric":
            self.units = "imperial"
            print("\n✅ Switched to Fahrenheit (°F)")
        else:
            self.units = "metric"
            print("\n✅ Switched to Celsius (°C)")
    
    def show_recent_searches(self):
        """Show recently searched cities"""
        if not self.recent_searches:
            print("\nNo recent searches.")
            return
        
        print("\n" + "=" * 40)
        print("RECENT SEARCHES")
        print("=" * 40)
        
        for i, city in enumerate(self.recent_searches, 1):
            print(f"{i}. {city}")
            
            # Quick weather summary
            if city in self.weather_data:
                weather = self.weather_data[city]["current"]
                temp = self.convert_temperature(weather["temp_c"])
                unit = self.get_temperature_unit()
                print(f"   {self.get_weather_emoji(weather['condition'])} {temp:.1f}{unit} - {weather['condition']}")
        
        print("=" * 40)
        
        # Option to select from recent
        try:
            choice = input("\nEnter number to view details (or press Enter to skip): ")
            if choice.strip():
                idx = int(choice) - 1
                if 0 <= idx < len(self.recent_searches):
                    city = self.recent_searches[idx]
                    weather = self.simulate_api_call(city)
                    if weather:
                        self.display_current_weather(city, weather)
        except (ValueError, IndexError):
            print("Invalid selection.")
    
    def manage_favorites(self):
        """Manage favorite cities"""
        print("\n" + "=" * 40)
        print("FAVORITE CITIES")
        print("=" * 40)
        
        if not self.favorite_cities:
            print("No favorite cities yet.")
        else:
            print("Your favorite cities:")
            for i, city in enumerate(self.favorite_cities, 1):
                print(f"{i}. {city}")
        
        print("\nOptions:")
        print("1. Add favorite city")
        print("2. Remove favorite city")
        print("3. View weather for favorites")
        print("4. Back to main menu")
        
        choice = input("\nEnter choice: ").strip()
        
        if choice == "1":
            city = self.get_city_input()
            if city and city not in self.favorite_cities:
                self.favorite_cities.append(city)
                print(f"✅ Added {city} to favorites!")
        elif choice == "2":
            if self.favorite_cities:
                for i, city in enumerate(self.favorite_cities, 1):
                    print(f"{i}. {city}")
                try:
                    idx = int(input("Enter number to remove: ")) - 1
                    if 0 <= idx < len(self.favorite_cities):
                        removed = self.favorite_cities.pop(idx)
                        print(f"✅ Removed {removed} from favorites.")
                except (ValueError, IndexError):
                    print("Invalid selection.")
            else:
                print("No favorites to remove.")
        elif choice == "3":
            if not self.favorite_cities:
                print("No favorite cities to display.")
            else:
                print("\nWeather for your favorite cities:")
                print("-" * 40)
                for city in self.favorite_cities:
                    if city in self.weather_data:
                        weather = self.weather_data[city]
                        temp = self.convert_temperature(weather["current"]["temp_c"])
                        unit = self.get_temperature_unit()
                        condition = weather["current"]["condition"]
                        emoji = self.get_weather_emoji(condition)
                        print(f"{city}: {emoji} {temp:.1f}{unit} - {condition}")
                    else:
                        print(f"{city}: Data not available")
        elif choice == "4":
            return
        else:
            print("Invalid choice.")
    
    def show_weather_alerts(self):
        """Show weather alerts"""
        print("\n" + "=" * 50)
        print("⚠  WEATHER ALERTS")
        print("=" * 50)
        
        # Simulated alerts
        alerts = [
            {"city": "London", "type": "Rain", "severity": "Moderate", "message": "Heavy rain expected tonight"},
            {"city": "New York", "type": "Wind", "severity": "High", "message": "Strong winds expected tomorrow"}
        ]
        
        if alerts:
            for alert in alerts:
                severity_emoji = "🟡" if alert["severity"] == "Moderate" else "🔴"
                print(f"\n{severity_emoji} {alert['city']} - {alert['type']} Alert ({alert['severity']})")
                print(f"   {alert['message']}")
                print(f"   Issued: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M')}")
        else:
            print("\n✅ No active weather alerts in your area.")
        
        print("\n" + "=" * 50)
    
    def check_current_weather(self):
        """Check current weather for a city"""
        city = self.get_city_input()
        if not city:
            return
        
        weather = self.simulate_api_call(city)
        if weather:
            self.display_current_weather(city, weather)
    
    def check_weather_forecast(self):
        """Check weather forecast for a city"""
        city = self.get_city_input()
        if not city:
            return
        
        weather = self.simulate_api_call(city)
        if weather:
            self.display_forecast(city, weather)
    
    def run(self):
        """Run the weather application"""
        print("\n🌤️  Welcome to the Weather App!")
        print("Get accurate weather information for any city.")
        
        while True:
            self.display_menu()
            
            try:
                choice = input("\nEnter your choice (1-7): ").strip()
                
                if choice == "1":
                    self.check_current_weather()
                elif choice == "2":
                    self.check_weather_forecast()
                elif choice == "3":
                    self.toggle_units()
                elif choice == "4":
                    self.show_recent_searches()
                elif choice == "5":
                    self.manage_favorites()
                elif choice == "6":
                    self.show_weather_alerts()
                elif choice == "7":
                    print("\n👋 Thank you for using Weather App!")
                    print("Stay safe and have a great day! 🌈")
                    break
                else:
                    print("\n❌ Invalid choice. Please enter 1-7.")
            except KeyboardInterrupt:
                print("\n\n👋 Goodbye!")
                break
            except Exception as e:
                print(f"\n❌ An error occurred: {e}")

# Run the application
if __name__ == "__main__":
    app = WeatherApp()
    app.run()