File size: 8,102 Bytes
d0624ee
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
"""
FarmGuard Nigeria - Plant Disease Detection API
Using Google Gemini Vision for accurate real photo analysis
"""
import json
import logging
import os
import base64
from google import genai
from google.genai import types
from dotenv import load_dotenv
from fastapi import FastAPI, File, UploadFile, HTTPException
from fastapi.middleware.cors import CORSMiddleware

# Load .env file
load_dotenv()

# Setup logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

app = FastAPI(
    title="FarmGuard Nigeria API",
    description="AI-powered plant disease detection for Nigerian farmers",
    version="2.0.0"
)

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["*"],
    allow_headers=["*"],
)

# Load disease config
config_path = os.path.join(os.path.dirname(__file__), "disease_config.json")
with open(config_path) as f:
    DISEASE_CONFIG = json.load(f)

# Setup Gemini
GEMINI_KEY = os.getenv("GEMINI_API_KEY")
gemini_client = None

if GEMINI_KEY:
    gemini_client = genai.Client(api_key=GEMINI_KEY)
    logger.info("Gemini Vision API ready")
else:
    logger.warning("No GEMINI_API_KEY found in .env file")


@app.get("/")
def root():
    return {
        "message": "FarmGuard Nigeria API is running!",
        "version": "2.0.0",
        "gemini_ready": gemini_client is not None,
        "docs": "/docs"
    }


@app.get("/api/health")
def health():
    return {
        "status": "healthy",
        "gemini_ready": gemini_client is not None,
    }


@app.post("/api/predict")
async def predict(file: UploadFile = File(...)):
    """Detect plant disease from uploaded leaf image using Gemini Vision."""

    if file.content_type not in ["image/jpeg", "image/jpg", "image/png"]:
        raise HTTPException(
            status_code=400,
            detail="Only JPEG and PNG images are supported"
        )

    image_bytes = await file.read()

    if len(image_bytes) > 8 * 1024 * 1024:
        raise HTTPException(
            status_code=400,
            detail="Image too large. Maximum size is 8MB"
        )

    if gemini_client is None:
        raise HTTPException(
            status_code=503,
            detail="Gemini API not configured. Add GEMINI_API_KEY to your .env file"
        )

    try:
        mime_type = file.content_type

        prompt = """You are an expert Nigerian agricultural plant pathologist.

Analyse this plant leaf image and respond ONLY with a JSON object in this exact format with no extra text:
{
  "crop_identified": true,
  "plant": "crop name e.g. Tomato, Maize, Cassava, Potato, Pepper, Yam, Plantain",
  "disease": "disease name or Healthy",
  "is_healthy": false,
  "confidence": 0.85,
  "severity": "none or low or moderate or high or very_high",
  "symptoms": ["symptom 1", "symptom 2", "symptom 3"],
  "causes": "what causes this disease",
  "treatment": {
    "chemical": "specific chemical treatment with Nigerian product names e.g. Dithane M-45, Cobox, Ridomil Gold, Confidor",
    "cultural": "cultural and physical management practices",
    "preventive": "prevention measures for Nigerian farmers"
  },
  "urgency": "treatment urgency description",
  "economic_impact": "potential yield and economic impact on Nigerian farmer",
  "message": ""
}

Rules:
- If you cannot see a plant leaf clearly, set crop_identified to false and explain in message
- Focus on crops grown in Nigeria: Tomato, Maize, Cassava, Potato, Pepper, Yam, Plantain, Rice, Cowpea, Soybean, Orange, Groundnut, Banana
- Always include locally available Nigerian product names in chemical treatment
- Be specific and practical for Nigerian smallholder farmers
- Respond with ONLY the JSON object, nothing else"""

        # Call Gemini with new SDK
        response = gemini_client.models.generate_content(
            model="gemini-2.5-flash",
            contents=[
                types.Part.from_bytes(data=image_bytes, mime_type=mime_type),
                prompt
            ]
        )

        text = response.text.strip()

        # Remove markdown code blocks if present
        if "```json" in text:
            text = text.split("```json")[1].split("```")[0].strip()
        elif "```" in text:
            text = text.split("```")[1].split("```")[0].strip()

        result = json.loads(text)

        # Handle unidentified image
        if not result.get("crop_identified", False):
            return {
                "success": False,
                "crop_identified": False,
                "message": result.get("message", "Could not identify a plant leaf. Please upload a clear close-up photo of a single leaf."),
                "top_prediction": None,
                "all_predictions": [],
                "is_healthy": False
            }

        severity = result.get("severity", "moderate")
        severity_colors = {
            "none": "#16a34a", "low": "#ca8a04",
            "moderate": "#ea580c", "high": "#dc2626", "very_high": "#7f1d1d"
        }

        top_prediction = {
            "plant": result.get("plant", "Unknown"),
            "disease": result.get("disease", "Unknown"),
            "confidence": float(result.get("confidence", 0.8)),
            "severity": severity,
            "severity_color": severity_colors.get(severity, "#ea580c"),
            "symptoms": result.get("symptoms", []),
            "causes": result.get("causes", ""),
            "treatment": result.get("treatment", {}),
            "urgency": result.get("urgency", ""),
            "economic_impact": result.get("economic_impact", "")
        }

        conf = float(result.get("confidence", 0.8))
        confidence_level = "high" if conf >= 0.7 else "medium" if conf >= 0.4 else "low"

        return {
            "success": True,
            "crop_identified": True,
            "is_healthy": result.get("is_healthy", False),
            "top_prediction": top_prediction,
            "all_predictions": [top_prediction],
            "confidence_level": confidence_level,
            "message": None
        }

    except json.JSONDecodeError as e:
        logger.error(f"JSON parse error: {e}")
        raise HTTPException(
            status_code=500,
            detail="Failed to parse AI response. Please try again."
        )
    except Exception as e:
        logger.error(f"Gemini error: {e}")
        raise HTTPException(
            status_code=500,
            detail=f"AI analysis failed: {str(e)}"
        )


@app.get("/api/recommend")
def recommend(state: str = "Rivers", soil: str = "loamy", season: str = "rainy"):
    """Get crop recommendations based on location, soil and season."""

    recommendations = {
        "rainy": {
            "crops": ["Cassava", "Yam", "Maize", "Plantain", "Cocoyam", "Waterleaf", "Fluted Pumpkin (Ugu)"],
            "tip": "Rainy season is ideal for root crops and leafy vegetables. Ensure good drainage to prevent waterlogging."
        },
        "dry": {
            "crops": ["Tomato", "Pepper", "Onion", "Carrot", "Cabbage", "Cowpea", "Soybean", "Groundnut"],
            "tip": "Dry season farming requires irrigation. Vegetables thrive well with consistent water supply."
        }
    }

    soil_tips = {
        "loamy": "Loamy soil is excellent and suitable for almost all Nigerian crops. Maintain organic matter with compost.",
        "sandy": "Sandy soil drains fast — best for cassava, groundnut and sweet potato. Add organic matter to improve water retention.",
        "clay": "Clay soil retains water well — suitable for rice and yam. Ensure adequate drainage ridges to avoid waterlogging."
    }

    season_data = recommendations.get(season.lower(), recommendations["rainy"])
    soil_tip = soil_tips.get(soil.lower(), "Prepare soil well with compost before planting.")

    return {
        "state": state,
        "season": season,
        "soil_type": soil,
        "recommended_crops": season_data["crops"],
        "season_tip": season_data["tip"],
        "soil_tip": soil_tip
    }


if __name__ == "__main__":
    import uvicorn
    uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)