kisaansaathi / app.py
agentsay's picture
Update app.py
dcb82e9 verified
from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.templating import Jinja2Templates
from fastapi.staticfiles import StaticFiles
import pandas as pd
from prophet import Prophet
import matplotlib.pyplot as plt
import os
import matplotlib
matplotlib.use("Agg")
from pydantic import BaseModel
import json
app = FastAPI()
# Mount static files
app.mount("/static", StaticFiles(directory="static"), name="static")
# Templates
templates = Jinja2Templates(directory="templates")
UPLOAD_FOLDER = 'static'
if not os.path.exists(UPLOAD_FOLDER):
os.makedirs(UPLOAD_FOLDER)
# Load data
yield_file = 'ICRISAT-District_Level_Data_30_Years.csv'
soil_file = 'SoilHealthScores_by_District_2.csv'
yield_df = pd.read_csv(yield_file)
soil_df = pd.read_csv(soil_file)
# Helper functions
def get_soil_category(score):
if score == 0:
return "No Soil Health Data"
elif score >= 4.5:
return "Very Excellent Soil Health"
elif score >= 4:
return "Excellent Soil Health"
elif score >= 3:
return "Good Soil Health"
elif score >= 2:
return "Poor Soil Health"
else:
return "Very Poor Soil Health"
def calculate_climate_score(yield_cat, soil_cat):
score_map = {
"Highly Recommended Crop": 90,
"Good Crop": 70,
"Poor Crop": 50,
"Very Poor Crop": 30,
"Very Excellent Soil Health": 95,
"Excellent Soil Health": 85,
"Good Soil Health": 65,
"Poor Soil Health": 45,
"Very Poor Soil Health": 25,
"No Soil Health Data": 0
}
return int((score_map[yield_cat] * 0.6) + (score_map[soil_cat] * 0.4))
soil_df['Soil_Category'] = soil_df['SoilHealthScore'].apply(get_soil_category)
yield_columns = [col for col in yield_df.columns if 'YIELD (Kg per ha)' in col]
base_crop_names = {col.split(' YIELD')[0]: col for col in yield_columns}
def calculate_loan(c, predicted_yield, yield_cat, soil_cat, climate_score):
crop_base_prices_per_hectare = {
"RICE": 75000,
"WHEAT": 65000,
"KHARIF SORGHUM": 60000,
"RABI SORGHUM": 62000,
"SORGHUM": 61000,
"PEARL MILLET": 50000,
"MAIZE": 55000,
"FINGER MILLET": 77000,
"BARLEY": 48000,
"CHICKPEA": 90000,
"PIGEONPEA": 95000,
"MINOR PULSES": 85000,
"GROUNDNUT": 110000,
"SESAMUM": 130000,
"RAPESEED AND MUSTARD": 100000,
"SAFFLOWER": 95000,
"CASTOR": 88000,
"LINSEED": 90000,
"SUNFLOWER": 102000,
"SOYABEAN": 98000,
"OILSEEDS": 94000,
"SUGARCANE": 150000,
"COTTON": 120000
}
base_loan = crop_base_prices_per_hectare[c]
yield_multiplier = {
"Highly Recommended Crop": 1.5,
"Good Crop": 1.2,
"Poor Crop": 0.8,
"Very Poor Crop": 0.5
}
soil_multiplier = {
"Very Excellent Soil Health": 1.5,
"Excellent Soil Health": 1.3,
"Good Soil Health": 1.1,
"Poor Soil Health": 0.9,
"Very Poor Soil Health": 0.7,
"No Soil Health Data": 0.5
}
climate_multiplier = climate_score / 100
loan_amount = base_loan * yield_multiplier[yield_cat] * soil_multiplier[soil_cat] * climate_multiplier
return round(loan_amount, 2)
@app.get("/", response_class=HTMLResponse)
async def home(request: Request):
return templates.TemplateResponse("index.html", {"request": request, "crops": base_crop_names.keys()})
@app.get("/api/crops")
async def get_crops():
return {"crops": list(base_crop_names.keys())}
class PredictInput(BaseModel):
crop: str
district: str
land_area: str
@app.post("/predict", response_class=HTMLResponse)
async def predict(request: Request, input_data: PredictInput):
crop_input = input_data.crop
district_input = input_data.district
land_area = input_data.land_area
if not crop_input or not district_input or crop_input not in base_crop_names:
raise HTTPException(status_code=400, detail="Invalid input. Please enter a valid crop and district.")
yield_col = base_crop_names[crop_input]
district_yield = yield_df[yield_df['Dist Name'] == district_input]
district_soil = soil_df[soil_df['Dist Name'] == district_input]
if district_yield.empty or district_soil.empty:
raise HTTPException(status_code=400, detail="District data not found.")
ts_data = district_yield[['Year', yield_col]].dropna()
ts_data.columns = ['ds', 'y']
ts_data['ds'] = pd.to_datetime(ts_data['ds'], format='%Y')
model = Prophet(yearly_seasonality=True, growth='flat')
model.fit(ts_data)
future = model.make_future_dataframe(periods=1, freq='YS')
forecast = model.predict(future)
predicted_yield = max(forecast.iloc[-1]['yhat'], 0)
if predicted_yield > 1000:
yield_cat = "Highly Recommended Crop"
color = "green"
elif predicted_yield > 500:
yield_cat = "Good Crop"
color = "yellow"
elif predicted_yield > 200:
yield_cat = "Poor Crop"
color = "orange"
else:
yield_cat = "Very Poor Crop"
color = "red"
soil_score = district_soil['SoilHealthScore'].values[0]
soil_cat = district_soil['Soil_Category'].values[0]
climate_score = calculate_climate_score(yield_cat, soil_cat)
plt.figure(figsize=(10, 5))
model.plot(forecast)
image_path = os.path.join(UPLOAD_FOLDER, "forecast.png")
plt.savefig(image_path)
plt.close()
loan_amount = calculate_loan(crop_input, predicted_yield, yield_cat, soil_cat, climate_score)
best_crop = None
max_yield = 0
for crop, column in base_crop_names.items():
ts_data = district_yield[['Year', column]].dropna()
ts_data.columns = ['ds', 'y']
ts_data['ds'] = pd.to_datetime(ts_data['ds'], format='%Y')
if len(ts_data) >= 5:
model = Prophet(yearly_seasonality=True, growth='flat')
model.fit(ts_data)
future = model.make_future_dataframe(periods=1, freq='YS')
forecast = model.predict(future)
predicted_yield = max(forecast.iloc[-1]['yhat'], 0)
if predicted_yield > max_yield:
max_yield = predicted_yield
best_crop = crop
plt.figure(figsize=(10, 5))
ts_data = district_yield[['Year', yield_col]].dropna()
ts_data.columns = ['ds', 'y']
ts_data['ds'] = pd.to_datetime(ts_data['ds'], format='%Y')
plt.plot(ts_data['ds'], ts_data['y'], label=f'{crop_input} Yield')
best_crop_data = district_yield[['Year', base_crop_names[best_crop]]].dropna()
best_crop_data.columns = ['ds', 'y']
best_crop_data['ds'] = pd.to_datetime(best_crop_data['ds'], format='%Y')
plt.plot(best_crop_data['ds'], best_crop_data['y'], label=f'{best_crop} Yield', linestyle='--')
plt.xlabel('Year')
plt.ylabel('Yield (Kg/ha)')
plt.title(f"Yield Comparison for {crop_input} and Best Crop ({best_crop}) in {district_input}")
plt.legend()
image_path2 = os.path.join(UPLOAD_FOLDER, "forecast2.png")
plt.grid(True)
plt.savefig(image_path2)
plt.close()
result = {
"crop": crop_input,
"district": district_input,
"predicted_yield": f"{round(predicted_yield, 2)}Kg/ha",
"loan_amount": float(loan_amount) * float(land_area),
"best_crop": best_crop,
"yield_cat": yield_cat,
"color": color,
"soil_health": soil_cat,
"climate_score": climate_score
}
return templates.TemplateResponse("index.html", {
"request": request,
"result": result,
"image_path": image_path,
"image_path2": image_path2,
"crops": base_crop_names.keys()
})
@app.get("/api/predict")
async def api_predict(crop: str, district: str, land: str):
if not crop or not district:
raise HTTPException(status_code=400, detail="Missing crop or district in request.")
return await predict2(crop, district, land)
async def predict2(crop_input: str, district_input: str, area: str):
if not crop_input or not district_input or crop_input not in base_crop_names:
raise HTTPException(status_code=400, detail="Invalid input. Please enter a valid crop and district.")
yield_col = base_crop_names[crop_input]
district_yield = yield_df[yield_df['Dist Name'] == district_input]
district_soil = soil_df[soil_df['Dist Name'] == district_input]
if district_yield.empty or district_soil.empty:
raise HTTPException(status_code=400, detail="District data not found.")
ts_data = district_yield[['Year', yield_col]].dropna()
ts_data.columns = ['ds', 'y']
ts_data['ds'] = pd.to_datetime(ts_data['ds'], format='%Y')
model = Prophet(yearly_seasonality=True, growth='flat')
model.fit(ts_data)
future = model.make_future_dataframe(periods=1, freq='YS')
forecast = model.predict(future)
predicted_yield = max(forecast.iloc[-1]['yhat'], 0)
if predicted_yield > 1000:
yield_cat = "Highly Recommended Crop"
color = "green"
elif predicted_yield > 500:
yield_cat = "Good Crop"
color = "yellow"
elif predicted_yield > 200:
yield_cat = "Poor Crop"
color = "orange"
else:
yield_cat = "Very Poor Crop"
color = "red"
soil_score = district_soil['SoilHealthScore'].values[0]
soil_cat = district_soil['Soil_Category'].values[0]
climate_score = calculate_climate_score(yield_cat, soil_cat)
loan_amount = calculate_loan(crop_input, predicted_yield, yield_cat, soil_cat, climate_score)
best_crop = None
max_yield = 0
for crop, column in base_crop_names.items():
ts_data = district_yield[['Year', column]].dropna()
ts_data.columns = ['ds', 'y']
ts_data['ds'] = pd.to_datetime(ts_data['ds'], format='%Y')
if len(ts_data) >= 5:
model = Prophet(yearly_seasonality=True, growth='flat')
model.fit(ts_data)
future = model.make_future_dataframe(periods=1, freq='YS')
forecast = model.predict(future)
predicted_yield = max(forecast.iloc[-1]['yhat'], 0)
if predicted_yield > max_yield:
max_yield = predicted_yield
best_crop = crop
top_crops = []
crop_yields = []
for crop, column in base_crop_names.items():
ts_data = district_yield[['Year', column]].dropna()
ts_data.columns = ['ds', 'y']
ts_data['ds'] = pd.to_datetime(ts_data['ds'], format='%Y')
if len(ts_data) >= 5:
model = Prophet(yearly_seasonality=True, growth='flat')
model.fit(ts_data)
future = model.make_future_dataframe(periods=1, freq='YS')
forecast = model.predict(future)
predicted_yield = max(forecast.iloc[-1]['yhat'], 0)
crop_yields.append((crop, predicted_yield))
top_crops = sorted(crop_yields, key=lambda x: x[1], reverse=True)[:3]
top_crops_array = [crop for crop, yield_value in top_crops]
ts_data_json = [{"Year": str(year.year), "Yield": yield_value} for year, yield_value in zip(ts_data['ds'], ts_data['y'])]
best_crop_data = district_yield[['Year', base_crop_names[best_crop]]].dropna()
best_crop_data.columns = ['ds', 'y']
best_crop_data['ds'] = pd.to_datetime(best_crop_data['ds'], format='%Y')
best_crop_data_json = [{"Year": str(year.year), "Yield": yield_value} for year, yield_value in zip(best_crop_data['ds'], best_crop_data['y'])]
result = {
"crop": crop_input,
"district": district_input,
"predicted_yield": f"{round(predicted_yield, 2)} Kg/ha",
"yield_category": yield_cat,
"best_crop": top_crops_array,
"soil_health": soil_cat,
"score": climate_score,
"loan_amount": f"{float(loan_amount)*float(area)}",
}
return result
@app.get("/api/map")
async def api_map(crop: str, district: str, land: str):
if not crop or not district:
raise HTTPException(status_code=400, detail="Missing crop or district in request.")
return await map_data(crop, district, land)
async def map_data(crop_input: str, district_input: str, area: str):
if not crop_input or not district_input or crop_input not in base_crop_names:
raise HTTPException(status_code=400, detail="Invalid input. Please enter a valid crop and district.")
yield_col = base_crop_names[crop_input]
district_yield = yield_df[yield_df['Dist Name'] == district_input]
district_soil = soil_df[soil_df['Dist Name'] == district_input]
if district_yield.empty or district_soil.empty:
raise HTTPException(status_code=400, detail="District data not found.")
ts_data = district_yield[['Year', yield_col]].dropna()
ts_data.columns = ['ds', 'y']
ts_data['ds'] = pd.to_datetime(ts_data['ds'], format='%Y')
model = Prophet(yearly_seasonality=True, growth='flat')
model.fit(ts_data)
future = model.make_future_dataframe(periods=1, freq='YS')
forecast = model.predict(future)
predicted_yield = max(forecast.iloc[-1]['yhat'], 0)
best_crop = None
max_yield = 0
for crop, column in base_crop_names.items():
ts_data = district_yield[['Year', column]].dropna()
ts_data.columns = ['ds', 'y']
ts_data['ds'] = pd.to_datetime(ts_data['ds'], format='%Y')
if len(ts_data) >= 5:
model = Prophet(yearly_seasonality=True, growth='flat')
model.fit(ts_data)
future = model.make_future_dataframe(periods=1, freq='YS')
forecast = model.predict(future)
predicted_yield = max(forecast.iloc[-1]['yhat'], 0)
if predicted_yield > max_yield:
max_yield = predicted_yield
best_crop = crop
ts_data_json = [{"Year": str(year.year), "Yield": yield_value} for year, yield_value in zip(ts_data['ds'], ts_data['y'])]
best_crop_data = district_yield[['Year', base_crop_names[best_crop]]].dropna()
best_crop_data.columns = ['ds', 'y']
best_crop_data['ds'] = pd.to_datetime(best_crop_data['ds'], format='%Y')
best_crop_data_json = [{"Year": str(year.year), "Yield": yield_value} for year, yield_value in zip(best_crop_data['ds'], best_crop_data['y'])]
result = {
"ts_data": ts_data_json,
"best_crop_data": best_crop_data_json
}
return result
# To run the application:
# if __name__ == "__main__":
# import uvicorn
# uvicorn.run(app, host="0.0.0.0", port=8000)