Metadata-Version: 2.4
Name: agrivisor
Version: 1.0.2
Summary: Agriculture advisory engine for weather-based recommendations
Author-email: Muzammil Sarwar <x24212857@student.ncirl.ie>
License-Expression: MIT
Keywords: agriculture,weather,advisory,crop-recommendation,farming
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pydantic>=1.10.0
Requires-Dist: requests>=2.28.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
Requires-Dist: black>=23.0.0; extra == "dev"
Requires-Dist: flake8>=6.0.0; extra == "dev"
Requires-Dist: mypy>=1.0.0; extra == "dev"
Dynamic: license-file

# AgriVisor - OpenWeather API Integration Library

A Python library for fetching weather data from OpenWeather APIs, designed for agricultural and forest management simulations.

## Features

- 🌤️ **Current Weather** - Get real-time weather data for any location
- 📅 **Hourly Forecast** - 4-day hourly weather forecast (96 hours)
- 📆 **Daily Forecast** - 16-day daily weather forecast
- 🌬️ **Air Pollution** - Current air quality and pollution data
- 🗺️ **Geocoding** - Convert city names to coordinates and vice versa

## Installation

```bash
pip install agrivisor
```

Or install from source:

```bash
git clone https://github.com/yourusername/agrivisor-lib.git
cd agrivisor-lib
pip install -e .
```

## Quick Start

```python
from agrivisor import OpenWeatherClient

# Initialize the client with your API key
client = OpenWeatherClient(api_key="your_api_key_here")

# Get current weather for Dublin, Ireland
weather = client.get_current_weather(lat=53.3498, lon=-6.2603)
print(f"Temperature: {weather.temperature}°C")
print(f"Humidity: {weather.humidity}%")
print(f"Weather: {weather.weather_description}")
```

## API Reference

### OpenWeatherClient

The main client class for interacting with OpenWeather APIs.

**Initialization:**
```python
client = OpenWeatherClient(
    api_key: str,           # Required: Your OpenWeather API key
    units: str = "metric",   # Optional: "metric", "imperial", or "kelvin"
    language: str = "en"     # Optional: Language code for descriptions
)
```

**Key Methods:**

All methods support both coordinate-based and city name-based lookups:

- `get_current_weather(lat, lon)` or `get_current_weather(city_name, country_code)` - Get current weather
- `get_hourly_forecast(lat, lon, cnt)` or `get_hourly_forecast(city_name, country_code, cnt)` - Get hourly forecast (4 days)
- `get_daily_forecast(lat, lon, cnt)` or `get_daily_forecast(city_name, country_code, cnt)` - Get daily forecast (16 days)
- `get_air_pollution(lat, lon)` or `get_air_pollution(city_name, country_code)` - Get air pollution data
- `geocode(city_name, state_code, country_code, limit)` - Convert city name to coordinates
- `reverse_geocode(lat, lon, limit)` - Convert coordinates to city name
- `get_weather_snapshot(lat, lon)` or `get_weather_snapshot(city_name, country_code)` - Get simplified weather for games
- `is_raining(lat, lon)` or `is_raining(city_name, country_code)` - Check if it's raining
- `get_weather_summary(lat, lon)` or `get_weather_summary(city_name, country_code)` - Get human-readable summary

## Usage Examples

### Current Weather

```python
from agrivisor import OpenWeatherClient

client = OpenWeatherClient(api_key="your_api_key")

# Get current weather by coordinates
weather = client.get_current_weather(lat=53.3498, lon=-6.2603)

# Or get weather by city name
weather = client.get_current_weather(city_name="Dublin", country_code="IE")

print(f"Location: {weather.location_name}, {weather.country}")
print(f"Temperature: {weather.temperature}°C")
print(f"Feels like: {weather.feels_like}°C")
print(f"Humidity: {weather.humidity}%")
print(f"Pressure: {weather.pressure} hPa")
print(f"Wind Speed: {weather.wind_speed} m/s")
print(f"Weather: {weather.weather_main} - {weather.weather_description}")
print(f"Rain (1h): {weather.rain_1h} mm" if weather.rain_1h else "No rain")

# Get a simplified weather snapshot for game use
snapshot = client.get_weather_snapshot(lat=53.3498, lon=-6.2603)
# Returns: {'temp': 18.5, 'humidity': 70, 'rain': True, 'wind': 5.2, 'updatedAt': '...'}

# Check if it's raining
if client.is_raining(lat=53.3498, lon=-6.2603):
    print("It's raining!")

# Get a human-readable summary
summary = client.get_weather_summary(city_name="Dublin", country_code="IE")
print(summary)  # "Dublin, IE: 18.5°C, Partly Cloudy, Humidity: 70%, Wind: 5.2 m/s"
```

### Hourly Forecast (4 days)

```python
# Get hourly forecast by coordinates
forecasts = client.get_hourly_forecast(lat=53.3498, lon=-6.2603)

# Or by city name
forecasts = client.get_hourly_forecast(city_name="Dublin", country_code="IE", cnt=24)

# Get first 24 hours
for forecast in forecasts[:24]:
    print(f"{forecast.datetime}: {forecast.temperature}°C, {forecast.weather_description}")
    if forecast.rain_3h:
        print(f"  Rain: {forecast.rain_3h} mm")
```

### Daily Forecast (16 days)

```python
# Get daily forecast by coordinates
daily_forecasts = client.get_daily_forecast(lat=53.3498, lon=-6.2603, cnt=7)

# Or by city name
daily_forecasts = client.get_daily_forecast(city_name="Dublin", country_code="IE", cnt=7)

for forecast in daily_forecasts:
    print(f"{forecast.date}:")
    print(f"  High: {forecast.temp_max}°C, Low: {forecast.temp_min}°C")
    print(f"  Weather: {forecast.weather_description}")
    print(f"  Precipitation chance: {forecast.pop * 100}%")
    if forecast.rain:
        print(f"  Rain: {forecast.rain} mm")
```

### Air Pollution

```python
# Get air pollution data by coordinates
pollution = client.get_air_pollution(lat=53.3498, lon=-6.2603)

# Or by city name
pollution = client.get_air_pollution(city_name="Dublin", country_code="IE")

print(f"Air Quality Index: {pollution.aqi} ({pollution.aqi_description})")
print(f"PM2.5: {pollution.pm2_5} μg/m³")
print(f"PM10: {pollution.pm10} μg/m³")
print(f"Ozone (O3): {pollution.o3} μg/m³")
print(f"Carbon Monoxide (CO): {pollution.co} μg/m³")
```

### Geocoding

```python
# Convert city name to coordinates
results = client.geocode("Dublin", country_code="IE")
if results:
    location = results[0]
    print(f"Found: {location.name}, {location.country}")
    print(f"Coordinates: {location.lat}, {location.lon}")

# Reverse geocoding (coordinates to city name)
results = client.reverse_geocode(lat=53.3498, lon=-6.2603)
if results:
    print(f"Location: {results[0].name}, {results[0].country}")
```

### Custom Units and Language

```python
# Use Fahrenheit instead of Celsius
client = OpenWeatherClient(
    api_key="your_api_key",
    units="imperial"  # Options: "metric", "imperial", "kelvin"
)

# Use different language for weather descriptions
client = OpenWeatherClient(
    api_key="your_api_key",
    language="es"  # Spanish
)
```

## Error Handling

The library provides specific exceptions for different error scenarios:

```python
from agrivisor import (
    OpenWeatherClient,
    InvalidAPIKeyError,
    LocationNotFoundError,
    APIRequestError,
    OpenWeatherAPIError
)

client = OpenWeatherClient(api_key="your_api_key")

try:
    weather = client.get_current_weather(lat=53.3498, lon=-6.2603)
except InvalidAPIKeyError:
    print("Invalid API key. Please check your OpenWeather API key.")
except LocationNotFoundError:
    print("Location not found.")
except APIRequestError as e:
    print(f"Request failed: {e}")
except OpenWeatherAPIError as e:
    print(f"API error: {e}")
```

## Data Models

All API responses are returned as Pydantic models with type validation:

- `CurrentWeather` - Current weather data
- `HourlyForecast` - Hourly forecast data
- `DailyForecast` - Daily forecast data
- `AirPollution` - Air pollution data
- `GeocodingResult` - Geocoding results

## Integration with AgriQuest: Farm Guardian

This library is designed to work with the AgriQuest: Farm Guardian simulation system:

```python
from agrivisor import OpenWeatherClient

# Initialize client with API key from environment or config
client = OpenWeatherClient(api_key=os.getenv("OPENWEATHER_API_KEY"))

# In your game turn logic:
def process_turn(farm_lat, farm_lon):
    # Get current weather
    current = client.get_current_weather(lat=farm_lat, lon=farm_lon)
    
    # Get forecast for next few days
    hourly = client.get_hourly_forecast(lat=farm_lat, lon=farm_lon, cnt=24)
    daily = client.get_daily_forecast(lat=farm_lat, lon=farm_lon, cnt=7)
    
    # Use weather data to influence game mechanics:
    # - Soil moisture changes based on rain
    # - Crop growth affected by temperature
    # - Forest health influenced by air quality
    # - Random events triggered by weather conditions
    
    return {
        "current_weather": current,
        "hourly_forecast": hourly,
        "daily_forecast": daily,
    }
```

## API Endpoints Used

This library uses the following OpenWeather API endpoints:

- **Current Weather**: `/data/2.5/weather`
- **Hourly Forecast (4 days)**: `/data/2.5/forecast`
- **Daily Forecast (16 days)**: `/data/3.0/onecall` (with fallback to hourly aggregation)
- **Air Pollution**: `/data/2.5/air_pollution`
- **Geocoding**: `/geo/1.0/direct` and `/geo/1.0/reverse`

