AgroVision-Backend / routes /predictions.py
ShadowGard3n's picture
New endpoint
1c05a59
# from fastapi import APIRouter, HTTPException, Query
# from typing import List
# from schemas.prediction_schemas import CropPredictionRequest, FertilizerPredictionRequest
# from services.prediction_services import get_crop_prediction, get_fertilizer_prediction
# from services.market_services import get_market_prediction, _create_features
# from services.marketTracking_services import fetch_market_data
# from schemas.marketTracker_schemas import MarketPriceRequest, MarketPriceData
# from schemas.weather_schemas import WeatherResponse, ForecastResponse, DayForecast
# from services.weather_service import get_weather_data_for_city, AIR_QUALITY_MAP, get_weather_forecast_for_city
# import os
# import joblib
# import pandas as pd
# router = APIRouter()
# MODELS_DIR = 'models'
# models = {}
# # Ensure models dir exists
# if os.path.exists(MODELS_DIR):
# for model_file in os.listdir(MODELS_DIR):
# if model_file.endswith('.pkl'):
# commodity_name = model_file.replace('.pkl', '').replace('_', '/')
# models[commodity_name] = joblib.load(os.path.join(MODELS_DIR, model_file))
# print(f"✅ Model loaded for: {commodity_name}")
# try:
# # Ensure your CSV is accessible
# DF_FULL = pd.read_csv('final_output.csv', parse_dates=['created_at'], index_col='created_at')
# print("✅ Dataset loaded.")
# except FileNotFoundError:
# print("❌ 'final_output.csv' not found. Predictions will fail.")
# DF_FULL = None
# @router.post("/api/predict_crop")
# def predict_crop(request: CropPredictionRequest):
# return get_crop_prediction(request)
# @router.post("/api/predict_fertilizer")
# def predict_fertilizer(request: FertilizerPredictionRequest):
# return get_fertilizer_prediction(request)
# @router.get("/api/predict/{commodity}")
# def predict_commodity_price(commodity: str):
# # result = get_market_prediction(commodity)
# # if "error" in result:
# # raise HTTPException(status_code=404, detail=result["error"])
# # return result
# if DF_FULL is None:
# raise HTTPException(status_code=500, detail="Server Error: Dataset not loaded.")
# # 2. Check if Model exists (Normalize to Upper Case)
# target_commodity = commodity.upper()
# if target_commodity not in models:
# raise HTTPException(status_code=404, detail=f"Model for '{commodity}' not found.")
# model = models[target_commodity]
# # 3. Check if we have history for this commodity
# df_commodity = DF_FULL[DF_FULL['commodity'].str.upper() == target_commodity]
# if df_commodity.empty:
# raise HTTPException(status_code=404, detail="No historical data found for commodity")
# # 4. Get the last known date
# df_daily = df_commodity.groupby(df_commodity.index).agg({'modal_price': 'mean'})
# last_known_date = df_daily.index.max()
# # 5. Generate Recent History (for comparison chart)
# # Get last 90 days of actual data
# start_context_date = last_known_date - pd.Timedelta(days=90)
# df_featured = _create_features(df_daily)
# test_df = df_featured.loc[df_featured.index >= start_context_date]
# recent_data = []
# if not test_df.empty:
# FEATURES = [col for col in test_df.columns if col != 'modal_price']
# try:
# predictions = model.predict(test_df[FEATURES])
# for date, actual, pred in zip(test_df.index, test_df['modal_price'], predictions):
# recent_data.append({
# "date": date.strftime('%Y-%m-%d'),
# "actual_price": float(actual),
# "predicted_price": float(pred)
# })
# except Exception as e:
# print(f"Warning: Could not generate history validation: {e}")
# # 6. Generate Future Forecast (Calling the helper function correctly!)
# try:
# # HERE IS THE FIX: We pass all 4 arguments required by the helper
# daily_forecast_df = get_market_prediction(model, DF_FULL, target_commodity, last_known_date)
# future_data = []
# for date, row in daily_forecast_df.iterrows():
# future_data.append({
# "date": date.strftime('%Y-%m-%d'),
# "forecast_price": float(row['forecast'])
# })
# except Exception as e:
# print(f"Forecast Error: {e}")
# raise HTTPException(status_code=500, detail=f"Prediction failed: {str(e)}")
# return {
# "commodity": commodity,
# "recent_data": recent_data,
# "forecast_data": future_data
# }
# @router.post(
# "/api/marketPrice",
# response_model=List[MarketPriceData],
# summary="Fetch Agricultural Market Prices",
# description="Retrieves daily market price data for a specific commodity, state, and APMC over the last 7 days."
# )
# async def get_market_price(request: MarketPriceRequest):
# market_data = await fetch_market_data(request)
# return market_data
# @router.get("/weather/{city}", response_model=WeatherResponse)
# async def get_current_weather(city: str):
# try:
# weather_data = await get_weather_data_for_city(city)
# current_data = weather_data.get("current", {})
# location_data = weather_data.get("location", {})
# air_quality_data = current_data.get("air_quality", {})
# aqi_index = air_quality_data.get("us-epa-index")
# air_quality_description = AIR_QUALITY_MAP.get(aqi_index, "Unknown")
# response_data = WeatherResponse(
# location_name=location_data.get("name", "N/A"),
# temperature_c=current_data.get("temp_c"),
# condition=current_data.get("condition", {}).get("text", "N/A"),
# humidity=current_data.get("humidity"),
# wind_kph=current_data.get("wind_kph"),
# cloud=current_data.get("cloud"),
# is_day=current_data.get("is_day"),
# air_quality=air_quality_description
# )
# return response_data
# except HTTPException as e:
# raise e
# except Exception as e:
# raise HTTPException(status_code=500, detail=f"An unexpected error occurred: {str(e)}")
# @router.get("/weather/forecast/{city}", response_model=ForecastResponse, summary="Get Weather Forecast")
# async def get_weather_forecast(city: str, days: int = Query(default=1, ge=1, le=14, description="Number of days to forecast (between 1 and 14).")):
# """
# Retrieves the weather forecast for a specific city for a given number of days.
# """
# try:
# forecast_data = await get_weather_forecast_for_city(city, days)
# location_data = forecast_data.get("location", {})
# forecast_days_raw = forecast_data.get("forecast", {}).get("forecastday", [])
# processed_forecast_days = []
# for day_data in forecast_days_raw:
# day_details = day_data.get("day", {})
# processed_day = DayForecast(
# date=day_data.get("date"),
# maxtemp_c=day_details.get("maxtemp_c"),
# mintemp_c=day_details.get("mintemp_c"),
# avgtemp_c=day_details.get("avgtemp_c"),
# condition=day_details.get("condition", {}).get("text", "N/A"),
# daily_chance_of_rain=day_details.get("daily_chance_of_rain", 0),
# avghumidity=day_details.get("avghumidity", 0),
# maxwind_kph=day_details.get("maxwind_kph", 0.0),
# )
# processed_forecast_days.append(processed_day)
# response_data = ForecastResponse(
# location_name=location_data.get("name", "N/A"),
# forecast_days=processed_forecast_days
# )
# return response_data
# except HTTPException as e:
# raise e
# except Exception as e:
# raise HTTPException(status_code=500, detail=f"An unexpected error occurred: {str(e)}")
from fastapi import APIRouter, HTTPException, Query, UploadFile, File
from typing import List
from fastapi.responses import RedirectResponse
from schemas.prediction_schemas import CropPredictionRequest, FertilizerPredictionRequest
from services.prediction_services import get_crop_prediction, get_fertilizer_prediction
from services.market_services import get_market_prediction, _create_features
from services.marketTracking_services import fetch_market_data, fetch_historical_market_data
from schemas.marketTracker_schemas import MarketPriceRequest, MarketPriceData
from schemas.weather_schemas import WeatherResponse, ForecastResponse, DayForecast
from services.weather_service import get_weather_data_for_city, AIR_QUALITY_MAP, get_weather_forecast_for_city
from services.disease_service import get_tomato_disease_prediction
import os
import joblib
import pandas as pd
router = APIRouter()
MODELS_DIR = 'models'
models = {}
# Ensure models dir exists
if os.path.exists(MODELS_DIR):
for model_file in os.listdir(MODELS_DIR):
if model_file.endswith('.pkl'):
commodity_name = model_file.replace('.pkl', '').replace('_', '/')
models[commodity_name] = joblib.load(os.path.join(MODELS_DIR, model_file))
print(f"✅ Model loaded for: {commodity_name}")
try:
# Ensure your CSV is accessible
DF_FULL = pd.read_csv('final_output.csv', parse_dates=['created_at'], index_col='created_at')
print("✅ Dataset loaded.")
except FileNotFoundError:
print("❌ 'final_output.csv' not found. Predictions will fail.")
DF_FULL = None
@router.get("/", include_in_schema=False)
def root():
"""
Redirects the root URL to the deployed frontend.
"""
return RedirectResponse(url="https://agro-vision-frontend.vercel.app/")
@router.post("/api/predict_crop")
def predict_crop(request: CropPredictionRequest):
return get_crop_prediction(request)
@router.post("/api/predict_fertilizer")
def predict_fertilizer(request: FertilizerPredictionRequest):
return get_fertilizer_prediction(request)
# @router.get("/api/predict/{commodity}")
# def predict_commodity_price(commodity: str):
# if DF_FULL is None:
# raise HTTPException(status_code=500, detail="Server Error: Dataset not loaded.")
# # 2. Check if Model exists (Normalize to Upper Case)
# target_commodity = commodity.upper()
# if target_commodity not in models:
# raise HTTPException(status_code=404, detail=f"Model for '{commodity}' not found.")
# model = models[target_commodity]
# # 3. Check if we have history for this commodity
# df_commodity = DF_FULL[DF_FULL['commodity'].str.upper() == target_commodity]
# if df_commodity.empty:
# raise HTTPException(status_code=404, detail="No historical data found for commodity")
# # 4. Get the last known date
# df_daily = df_commodity.groupby(df_commodity.index).agg({'modal_price': 'mean'})
# last_known_date = df_daily.index.max()
# # 5. Generate Recent History (for comparison chart)
# start_context_date = last_known_date - pd.Timedelta(days=90)
# df_featured = _create_features(df_daily)
# test_df = df_featured.loc[df_featured.index >= start_context_date]
# recent_data = []
# if not test_df.empty:
# FEATURES = [col for col in test_df.columns if col != 'modal_price']
# try:
# # We try to disable feature check here too just in case
# try:
# model.get_booster().feature_names = None
# except:
# pass
# # Use values here as well to be safe
# input_values = test_df[FEATURES].values
# predictions = model.predict(input_values)
# for date, actual, pred in zip(test_df.index, test_df['modal_price'], predictions):
# recent_data.append({
# "date": date.strftime('%Y-%m-%d'),
# "actual_price": float(actual),
# "predicted_price": float(pred)
# })
# except Exception as e:
# print(f"Warning: Could not generate history validation: {e}")
# # 6. Generate Future Forecast
# try:
# # Calls the helper which now correctly returns a DataFrame
# daily_forecast_df = get_market_prediction(model, DF_FULL, target_commodity, last_known_date)
# future_data = []
# # YOUR ORIGINAL LOOP NOW WORKS BECAUSE IT'S A DATAFRAME AGAIN
# for date, row in daily_forecast_df.iterrows():
# future_data.append({
# "date": date.strftime('%Y-%m-%d'),
# "forecast_price": float(row['forecast'])
# })
# except Exception as e:
# print(f"Forecast Error: {e}")
# raise HTTPException(status_code=500, detail=f"Prediction failed: {str(e)}")
# # Returns the exact structure your frontend expects
# return {
# "commodity": commodity,
# "recent_data": recent_data,
# "forecast_data": future_data
# }
@router.get("/api/predict/{commodity}")
def predict_commodity_price(commodity: str):
# result = get_market_prediction(commodity)
# if "error" in result:
# raise HTTPException(status_code=404, detail=result["error"])
# return result
if DF_FULL is None:
raise HTTPException(status_code=500, detail="Server Error: Dataset not loaded.")
# 2. Check if Model exists (Normalize to Upper Case)
target_commodity = commodity.upper()
if target_commodity not in models:
raise HTTPException(status_code=404, detail=f"Model for '{commodity}' not found.")
model = models[target_commodity]
# 3. Check if we have history for this commodity
df_commodity = DF_FULL[DF_FULL['commodity'].str.upper() == target_commodity]
if df_commodity.empty:
raise HTTPException(status_code=404, detail="No historical data found for commodity")
# 4. Get the last known date
df_daily = df_commodity.groupby(df_commodity.index).agg({'modal_price': 'mean'})
last_known_date = df_daily.index.max()
# 5. Generate Recent History (for comparison chart)
# Get last 90 days of actual data
start_context_date = last_known_date - pd.Timedelta(days=90)
df_featured = _create_features(df_daily)
test_df = df_featured.loc[df_featured.index >= start_context_date]
recent_data = []
if not test_df.empty:
FEATURES = [col for col in test_df.columns if col != 'modal_price']
try:
predictions = model.predict(test_df[FEATURES])
for date, actual, pred in zip(test_df.index, test_df['modal_price'], predictions):
recent_data.append({
"date": date.strftime('%Y-%m-%d'),
"actual_price": float(actual),
"predicted_price": float(pred)
})
except Exception as e:
print(f"Warning: Could not generate history validation: {e}")
# 6. Generate Future Forecast (Calling the helper function correctly!)
try:
# HERE IS THE FIX: We pass all 4 arguments required by the helper
print(model, DF_FULL, target_commodity, last_known_date)
daily_forecast_df = get_market_prediction(model, DF_FULL, target_commodity, last_known_date)
print(daily_forecast_df)
future_data = []
for date, row in daily_forecast_df.iterrows():
future_data.append({
"date": date.strftime('%Y-%m-%d'),
"forecast_price": float(row['forecast'])
})
except Exception as e:
print(f"Forecast Error: {e}")
raise HTTPException(status_code=500, detail=f"Prediction failed: {str(e)}")
return {
"commodity": commodity,
"recent_data": recent_data,
"forecast_data": future_data
}
@router.post(
"/api/marketPrice",
response_model=List[MarketPriceData],
summary="Fetch Agricultural Market Prices",
description="Retrieves daily market price data for a specific commodity, state, and APMC over the last 7 days."
)
async def get_market_price(request: MarketPriceRequest):
market_data = await fetch_market_data(request)
return market_data
@router.get("/weather/{city}", response_model=WeatherResponse)
async def get_current_weather(city: str):
try:
weather_data = await get_weather_data_for_city(city)
current_data = weather_data.get("current", {})
location_data = weather_data.get("location", {})
air_quality_data = current_data.get("air_quality", {})
aqi_index = air_quality_data.get("us-epa-index")
air_quality_description = AIR_QUALITY_MAP.get(aqi_index, "Unknown")
response_data = WeatherResponse(
location_name=location_data.get("name", "N/A"),
temperature_c=current_data.get("temp_c"),
condition=current_data.get("condition", {}).get("text", "N/A"),
humidity=current_data.get("humidity"),
wind_kph=current_data.get("wind_kph"),
cloud=current_data.get("cloud"),
is_day=current_data.get("is_day"),
air_quality=air_quality_description
)
return response_data
except HTTPException as e:
raise e
except Exception as e:
raise HTTPException(status_code=500, detail=f"An unexpected error occurred: {str(e)}")
@router.get("/weather/forecast/{city}", response_model=ForecastResponse, summary="Get Weather Forecast")
async def get_weather_forecast(city: str, days: int = Query(default=1, ge=1, le=14, description="Number of days to forecast (between 1 and 14).")):
"""
Retrieves the weather forecast for a specific city for a given number of days.
"""
try:
forecast_data = await get_weather_forecast_for_city(city, days)
location_data = forecast_data.get("location", {})
forecast_days_raw = forecast_data.get("forecast", {}).get("forecastday", [])
processed_forecast_days = []
for day_data in forecast_days_raw:
day_details = day_data.get("day", {})
processed_day = DayForecast(
date=day_data.get("date"),
maxtemp_c=day_details.get("maxtemp_c"),
mintemp_c=day_details.get("mintemp_c"),
avgtemp_c=day_details.get("avgtemp_c"),
condition=day_details.get("condition", {}).get("text", "N/A"),
daily_chance_of_rain=day_details.get("daily_chance_of_rain", 0),
avghumidity=day_details.get("avghumidity", 0),
maxwind_kph=day_details.get("maxwind_kph", 0.0),
)
processed_forecast_days.append(processed_day)
response_data = ForecastResponse(
location_name=location_data.get("name", "N/A"),
forecast_days=processed_forecast_days
)
return response_data
except HTTPException as e:
raise e
except Exception as e:
raise HTTPException(status_code=500, detail=f"An unexpected error occurred: {str(e)}")
@router.post("/api/predict_disease/tomato")
async def predict_tomato_disease(file: UploadFile = File(...)):
"""
Accepts an image upload and returns bounding boxes and classifications
for tomato leaf diseases.
"""
if not file.content_type.startswith("image/"):
raise HTTPException(status_code=400, detail="Uploaded file must be an image.")
try:
image_bytes = await file.read()
prediction_result = get_tomato_disease_prediction(image_bytes)
return prediction_result
except RuntimeError as re:
raise HTTPException(status_code=503, detail=str(re))
except Exception as e:
print(f"Disease Prediction Error: {e}")
raise HTTPException(status_code=500, detail=f"Prediction failed: {str(e)}")
@router.post(
"/api/marketPrice/historical",
response_model=List[MarketPriceData],
summary="Fetch Historical Agricultural Market Prices",
description="Retrieves daily market price data for a specific commodity, state, and APMC over the last 90 days using parallel chunking."
)
async def get_historical_market_price(request: MarketPriceRequest):
market_data = await fetch_historical_market_data(request)
if not market_data:
raise HTTPException(status_code=404, detail="No historical market data found for the given parameters.")
return market_data