zidea21's picture
Update app.py
a754062 verified
Raw History Blame Contribute Delete
4.08 kB
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
import pandas as pd
import joblib
import os
import logging
from typing import Dict, List
# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
app = FastAPI(title="Fertilizer Recommendation API")
# Define model file paths
PIPELINE_PATH = r"fertilizer_pipeline.pkl"
ENCODER_PATH = r"fertilizer_label_encoder.pkl"
# Valid crops
VALID_CROPS = [
'Sugarcane', 'Jowar', 'Cotton', 'Rice', 'Wheat', 'Groundnut',
'Maize', 'Tur', 'Urad', 'Moong', 'Gram', 'Masoor', 'Soybean',
'Ginger', 'Turmeric', 'Grapes'
]
# Load pipeline and encoder at startup
try:
pipeline = joblib.load(PIPELINE_PATH)
label_encoder = joblib.load(ENCODER_PATH)
logger.info("Pipeline and encoder loaded successfully")
except Exception as e:
logger.error(f"Failed to load pipeline or encoder: {str(e)}")
raise Exception(f"Failed to load pipeline or encoder: {str(e)}")
# Pydantic model for input validation
class FertilizerInput(BaseModel):
Nitrogen: float = Field(..., ge=20, le=150, description="Nitrogen content in soil (kg/ha)")
Phosphorus: float = Field(..., ge=10, le=90, description="Phosphorus content in soil (kg/ha)")
Potassium: float = Field(..., ge=5, le=150, description="Potassium content in soil (kg/ha)")
pH: float = Field(..., ge=5.5, le=8.5, description="Soil pH value")
Rainfall: float = Field(..., ge=300, le=1700, description="Rainfall in millimeters")
Temperature: float = Field(..., ge=10, le=40, description="Temperature in Celsius")
Crop: str = Field(..., description="Crop type", enum=VALID_CROPS)
# Synchronous prediction function
def predict_fertilizer(input_data: Dict) -> Dict:
try:
# Convert input to DataFrame
input_df = pd.DataFrame([input_data])
# Validate required columns
required_cols = ['Nitrogen', 'Phosphorus', 'Potassium', 'pH', 'Rainfall', 'Temperature', 'Crop']
missing_cols = set(required_cols) - set(input_df.columns)
if missing_cols:
raise ValueError(f"Missing required columns: {missing_cols}")
# Predict
y_pred_encoded = pipeline.predict(input_df)
y_pred_label = label_encoder.inverse_transform(y_pred_encoded)[0]
return {
"fertilizer": y_pred_label,
"status": "success"
}
except Exception as e:
logger.error(f"Prediction error: {str(e)}")
return {
"fertilizer": "",
"status": "failure",
"error": str(e)
}
@app.post("/predict_fertilizer")
async def predict_fertilizer_endpoint(input_data: FertilizerInput):
try:
# Check if files exist
for path in [PIPELINE_PATH, ENCODER_PATH]:
if not os.path.exists(path):
raise HTTPException(status_code=500, detail=f"File not found: {path}")
# Convert Pydantic model to dict
input_dict = input_data.dict()
# Make prediction
result = predict_fertilizer(input_dict)
if result["status"] == "failure":
raise HTTPException(status_code=400, detail=result["error"])
return result
except Exception as e:
logger.error(f"Error processing prediction: {str(e)}")
raise HTTPException(status_code=500, detail=f"Error processing prediction: {str(e)}")
@app.get("/")
async def root():
return {"message": "Fertilizer Recommendation API is running. Use /predict_fertilizer endpoint to send input data."}
@app.get("/valid_inputs")
async def get_valid_inputs():
return {
"Nitrogen": {"min": 20, "max": 150, "unit": "kg/ha"},
"Phosphorus": {"min": 10, "max": 90, "unit": "kg/ha"},
"Potassium": {"min": 5, "max": 150, "unit": "kg/ha"},
"pH": {"min": 5.5, "max": 8.5, "unit": "pH"},
"Rainfall": {"min": 300, "max": 1700, "unit": "mm"},
"Temperature": {"min": 10, "max": 40, "unit": "Celsius"},
"Crop": VALID_CROPS
}