elanuk commited on
Commit
410b4b5
·
verified ·
1 Parent(s): ac68074

Update server.py

Browse files
Files changed (1) hide show
  1. server.py +372 -36
server.py CHANGED
@@ -108,36 +108,59 @@ async def generate_dynamic_alert(district: str, state: str):
108
  """Generate dynamic alert data using geographic functions and REAL weather data"""
109
 
110
  try:
111
- # Get villages for the district
112
  villages_data = await geographic_tools.list_villages(state, district)
113
 
114
- # Pick a random village or use default
115
- village_name = f"Village in {district}"
116
- if "villages" in villages_data and villages_data["villages"]:
117
- village_name = random.choice(villages_data["villages"])
118
- # Avoid village name being same as district
119
- if village_name.lower() == district.lower() and len(villages_data["villages"]) > 1:
120
- other_villages = [v for v in villages_data["villages"] if v.lower() != district.lower()]
121
- if other_villages:
122
- village_name = random.choice(other_villages)
123
 
124
- # Get coordinates for the district/village
125
- location_coords = [25.5941, 85.1376] # Default to Patna
 
 
126
 
127
- # Try to get coordinates for the district first
 
 
 
 
 
 
 
128
  try:
129
- district_location = await geographic_tools.reverse_geocode(district)
130
- if "error" not in district_location and "lat" in district_location:
131
- location_coords = [district_location["lat"], district_location["lng"]]
132
- except:
133
- pass # Keep default coordinates
 
 
 
 
 
 
 
 
 
 
 
 
 
134
 
135
- # Generate regional crop and stage
136
- regional_crop = get_regional_crop_for_area(district, state)
137
- crop_stage = get_current_crop_stage(regional_crop)
 
 
138
 
139
- # GET WEATHER DATA
 
 
 
 
140
  try:
 
 
141
  current_weather_data = await open_meteo.get_current_weather(
142
  latitude=location_coords[0],
143
  longitude=location_coords[1]
@@ -169,50 +192,63 @@ async def generate_dynamic_alert(district: str, state: str):
169
  "expected_rainfall": f"{next_3_days_rain:.1f}mm",
170
  "temperature": f"{current_temp:.1f}°C",
171
  "humidity": f"{estimated_humidity}%",
172
- "wind_speed": f"{current_windspeed:.1f} km/h"
 
173
  }
174
 
175
- # Generate alert message based on weather conditions
176
  if next_3_days_rain > 25:
177
  alert_type = "heavy_rain_warning"
178
  urgency = "high"
179
- alert_message = f"Heavy rainfall ({next_3_days_rain:.1f}mm) expected in next 3 days in {district}. Delay fertilizer application. Ensure proper drainage."
180
  action_items = ["delay_fertilizer", "check_drainage", "monitor_crops", "prepare_harvest_protection"]
181
  elif next_3_days_rain > 10:
182
  alert_type = "moderate_rain_warning"
183
  urgency = "medium"
184
- alert_message = f"Moderate rainfall ({next_3_days_rain:.1f}mm) expected in next 3 days in {district}. Monitor soil moisture levels."
185
  action_items = ["monitor_soil", "check_drainage", "adjust_irrigation"]
186
  elif next_3_days_rain < 2 and current_temp > 35:
187
  alert_type = "heat_drought_warning"
188
  urgency = "high"
189
- alert_message = f"High temperature ({current_temp:.1f}°C) with minimal rainfall expected in {district}. Increase irrigation frequency."
190
  action_items = ["increase_irrigation", "mulch_crops", "monitor_plant_stress"]
 
 
 
 
 
 
 
 
 
 
191
  else:
192
  alert_type = "weather_update"
193
  urgency = "low"
194
- alert_message = f"Normal weather conditions expected in {district}. Temperature {current_temp:.1f}°C, rainfall {next_3_days_rain:.1f}mm."
195
  action_items = ["routine_monitoring", "maintain_irrigation"]
196
 
197
- logger.info(f"Real weather data retrieved for {district}: {current_temp}°C, {next_3_days_rain:.1f}mm rain")
198
 
199
  except Exception as weather_error:
200
- logger.error(f"Failed to get real weather data for {district}: {weather_error}")
201
- raise Exception(f"Unable to retrieve current weather conditions for {district}")
202
 
203
  return {
204
- "alert_id": f"{state.upper()[:2]}_{district.upper()[:3]}_001_{datetime.now().strftime('%Y%m%d')}",
205
  "timestamp": datetime.now().isoformat() + "Z",
206
  "location": {
207
- "village": village_name,
208
  "district": district,
209
  "state": state.capitalize(),
210
- "coordinates": location_coords
 
 
211
  },
212
  "crop": {
213
  "name": regional_crop,
214
  "stage": crop_stage,
215
- "planted_estimate": "2025-06-15"
216
  },
217
  "alert": {
218
  "type": alert_type,
@@ -222,7 +258,7 @@ async def generate_dynamic_alert(district: str, state: str):
222
  "valid_until": (datetime.now() + timedelta(days=3)).isoformat() + "Z"
223
  },
224
  "weather": real_weather,
225
- "data_source": "open_meteo_api"
226
  }
227
 
228
  except Exception as e:
@@ -230,6 +266,306 @@ async def generate_dynamic_alert(district: str, state: str):
230
  raise Exception(f"Failed to generate weather alert for {district}: {str(e)}")
231
 
232
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
233
  @app.get("/")
234
  async def root():
235
  return {"message": "MCP Weather Server is running"}
 
108
  """Generate dynamic alert data using geographic functions and REAL weather data"""
109
 
110
  try:
111
+ # Step 1: Get villages for the district using your geographic tools
112
  villages_data = await geographic_tools.list_villages(state, district)
113
 
114
+ if "error" in villages_data:
115
+ raise Exception(f"District '{district}' not found in {state}")
 
 
 
 
 
 
 
116
 
117
+ # Step 2: Pick a random village from the actual list
118
+ available_villages = villages_data.get("villages", [])
119
+ if not available_villages:
120
+ raise Exception(f"No villages found for {district}")
121
 
122
+ selected_village = random.choice(available_villages)
123
+ logger.info(f"Selected village: {selected_village} from {len(available_villages)} villages")
124
+
125
+ # Step 3: Try to get coordinates for the selected village first, then district
126
+ location_coords = None
127
+ location_source = ""
128
+
129
+ # Try village coordinates first
130
  try:
131
+ village_location = await geographic_tools.reverse_geocode(selected_village)
132
+ if "error" not in village_location and "lat" in village_location:
133
+ location_coords = [village_location["lat"], village_location["lng"]]
134
+ location_source = f"village_{selected_village}"
135
+ logger.info(f"Using village coordinates for {selected_village}: {location_coords}")
136
+ except Exception as e:
137
+ logger.warning(f"Village geocoding failed for {selected_village}: {e}")
138
+
139
+ # Fallback to district coordinates if village lookup failed
140
+ if not location_coords:
141
+ try:
142
+ district_location = await geographic_tools.reverse_geocode(district)
143
+ if "error" not in district_location and "lat" in district_location:
144
+ location_coords = [district_location["lat"], district_location["lng"]]
145
+ location_source = f"district_{district}"
146
+ logger.info(f"Using district coordinates for {district}: {location_coords}")
147
+ except Exception as e:
148
+ logger.warning(f"District geocoding failed for {district}: {e}")
149
 
150
+ # Final fallback - but this should rarely happen now
151
+ if not location_coords:
152
+ logger.warning(f"No coordinates found for {selected_village} or {district}, using default")
153
+ location_coords = [25.5941, 85.1376] # Patna fallback
154
+ location_source = "fallback_patna"
155
 
156
+ # Step 4: Generate regional crop and stage using crop calendar data
157
+ regional_crop = await get_regional_crop_for_area(district, state)
158
+ crop_stage = await get_current_crop_stage_dynamic(regional_crop, district)
159
+
160
+ # Step 5: GET REAL WEATHER DATA using the actual coordinates
161
  try:
162
+ logger.info(f"Fetching weather for coordinates: {location_coords} (source: {location_source})")
163
+
164
  current_weather_data = await open_meteo.get_current_weather(
165
  latitude=location_coords[0],
166
  longitude=location_coords[1]
 
192
  "expected_rainfall": f"{next_3_days_rain:.1f}mm",
193
  "temperature": f"{current_temp:.1f}°C",
194
  "humidity": f"{estimated_humidity}%",
195
+ "wind_speed": f"{current_windspeed:.1f} km/h",
196
+ "coordinates_source": location_source # Track where coords came from
197
  }
198
 
199
+ # Step 6: Generate alert message based on actual weather conditions
200
  if next_3_days_rain > 25:
201
  alert_type = "heavy_rain_warning"
202
  urgency = "high"
203
+ alert_message = f"Heavy rainfall ({next_3_days_rain:.1f}mm) expected in next 3 days near {selected_village}, {district}. Delay fertilizer application. Ensure proper drainage."
204
  action_items = ["delay_fertilizer", "check_drainage", "monitor_crops", "prepare_harvest_protection"]
205
  elif next_3_days_rain > 10:
206
  alert_type = "moderate_rain_warning"
207
  urgency = "medium"
208
+ alert_message = f"Moderate rainfall ({next_3_days_rain:.1f}mm) expected in next 3 days near {selected_village}, {district}. Monitor soil moisture levels."
209
  action_items = ["monitor_soil", "check_drainage", "adjust_irrigation"]
210
  elif next_3_days_rain < 2 and current_temp > 35:
211
  alert_type = "heat_drought_warning"
212
  urgency = "high"
213
+ alert_message = f"High temperature ({current_temp:.1f}°C) with minimal rainfall expected near {selected_village}, {district}. Increase irrigation frequency."
214
  action_items = ["increase_irrigation", "mulch_crops", "monitor_plant_stress"]
215
+ elif current_temp < 10:
216
+ alert_type = "cold_warning"
217
+ urgency = "medium"
218
+ alert_message = f"Low temperature ({current_temp:.1f}°C) expected near {selected_village}, {district}. Protect crops from cold damage."
219
+ action_items = ["protect_crops", "cover_seedlings", "adjust_irrigation_timing"]
220
+ elif current_windspeed > 30:
221
+ alert_type = "high_wind_warning"
222
+ urgency = "medium"
223
+ alert_message = f"High winds ({current_windspeed:.1f} km/h) expected near {selected_village}, {district}. Secure crop supports and structures."
224
+ action_items = ["secure_supports", "check_structures", "monitor_damage"]
225
  else:
226
  alert_type = "weather_update"
227
  urgency = "low"
228
+ alert_message = f"Normal weather conditions expected near {selected_village}, {district}. Temperature {current_temp:.1f}°C, rainfall {next_3_days_rain:.1f}mm."
229
  action_items = ["routine_monitoring", "maintain_irrigation"]
230
 
231
+ logger.info(f"Real weather data retrieved for {selected_village}, {district}: {current_temp}°C, {next_3_days_rain:.1f}mm rain (coords: {location_coords})")
232
 
233
  except Exception as weather_error:
234
+ logger.error(f"Failed to get real weather data for {selected_village}, {district}: {weather_error}")
235
+ raise Exception(f"Unable to retrieve current weather conditions for {selected_village}, {district}")
236
 
237
  return {
238
+ "alert_id": f"{state.upper()[:2]}_{district.upper()[:3]}_{selected_village.upper()[:3]}_{datetime.now().strftime('%Y%m%d_%H%M')}",
239
  "timestamp": datetime.now().isoformat() + "Z",
240
  "location": {
241
+ "village": selected_village,
242
  "district": district,
243
  "state": state.capitalize(),
244
+ "coordinates": location_coords,
245
+ "coordinates_source": location_source,
246
+ "total_villages_in_district": len(available_villages)
247
  },
248
  "crop": {
249
  "name": regional_crop,
250
  "stage": crop_stage,
251
+ "planted_estimate": "2025-06-15" # You could make this dynamic too
252
  },
253
  "alert": {
254
  "type": alert_type,
 
258
  "valid_until": (datetime.now() + timedelta(days=3)).isoformat() + "Z"
259
  },
260
  "weather": real_weather,
261
+ "data_source": "open_meteo_api_with_dynamic_location"
262
  }
263
 
264
  except Exception as e:
 
266
  raise Exception(f"Failed to generate weather alert for {district}: {str(e)}")
267
 
268
 
269
+
270
+
271
+ import random
272
+ from datetime import datetime, date
273
+
274
+ # Enhanced crop selection function using your crop calendar data
275
+ async def get_regional_crop_for_area(district: str, state: str):
276
+ """Get typical crop for the region based on season and district - now fully dynamic"""
277
+
278
+ if state.lower() != 'bihar':
279
+ return 'rice' # fallback for other states
280
+
281
+ current_month = datetime.now().month
282
+ current_season = get_current_season(current_month)
283
+
284
+ # Get crops that are currently in season using your crop calendar tools
285
+ try:
286
+ seasonal_crops_data = await crop_calendar_tools.get_prominent_crops('bihar', current_season)
287
+ if "error" not in seasonal_crops_data:
288
+ seasonal_crops = seasonal_crops_data.get('crops', [])
289
+ else:
290
+ seasonal_crops = []
291
+ except Exception as e:
292
+ logger.warning(f"Failed to get seasonal crops: {e}")
293
+ seasonal_crops = []
294
+
295
+ # District-specific crop preferences (what's commonly grown in each district)
296
+ district_crop_preferences = {
297
+ 'patna': {
298
+ 'primary': ['rice', 'wheat', 'potato'],
299
+ 'secondary': ['mustard', 'gram', 'barley'],
300
+ 'specialty': ['sugarcane']
301
+ },
302
+ 'gaya': {
303
+ 'primary': ['wheat', 'rice', 'gram'],
304
+ 'secondary': ['barley', 'lentil', 'mustard'],
305
+ 'specialty': ['arhar']
306
+ },
307
+ 'bhagalpur': {
308
+ 'primary': ['rice', 'maize', 'wheat'],
309
+ 'secondary': ['jute', 'urd', 'moong'],
310
+ 'specialty': ['groundnut']
311
+ },
312
+ 'muzaffarpur': {
313
+ 'primary': ['sugarcane', 'rice', 'wheat'],
314
+ 'secondary': ['potato', 'mustard'],
315
+ 'specialty': ['lentil']
316
+ },
317
+ 'darbhanga': {
318
+ 'primary': ['rice', 'wheat', 'maize'],
319
+ 'secondary': ['gram', 'arhar'],
320
+ 'specialty': ['bajra']
321
+ },
322
+ 'siwan': {
323
+ 'primary': ['rice', 'wheat'],
324
+ 'secondary': ['gram', 'lentil', 'pea'],
325
+ 'specialty': ['mustard']
326
+ },
327
+ 'begusarai': {
328
+ 'primary': ['rice', 'wheat'],
329
+ 'secondary': ['jute', 'mustard'],
330
+ 'specialty': ['moong', 'urd']
331
+ },
332
+ 'katihar': {
333
+ 'primary': ['maize', 'rice'],
334
+ 'secondary': ['jute', 'urd', 'moong'],
335
+ 'specialty': ['jowar', 'bajra']
336
+ },
337
+ 'vaishali': {
338
+ 'primary': ['rice', 'wheat', 'sugarcane'],
339
+ 'secondary': ['potato', 'gram'],
340
+ 'specialty': ['mustard']
341
+ },
342
+ 'madhubani': {
343
+ 'primary': ['rice', 'wheat', 'maize'],
344
+ 'secondary': ['gram', 'lentil'],
345
+ 'specialty': ['arhar']
346
+ }
347
+ }
348
+
349
+ # Get district preferences or use default
350
+ district_prefs = district_crop_preferences.get(district.lower(), {
351
+ 'primary': ['rice', 'wheat'],
352
+ 'secondary': ['gram', 'mustard'],
353
+ 'specialty': ['maize']
354
+ })
355
+
356
+ # Combine all possible crops for this district
357
+ all_district_crops = (district_prefs.get('primary', []) +
358
+ district_prefs.get('secondary', []) +
359
+ district_prefs.get('specialty', []))
360
+
361
+ # Find crops that are both seasonal AND grown in this district
362
+ suitable_crops = []
363
+ if seasonal_crops:
364
+ suitable_crops = [crop for crop in all_district_crops if crop in seasonal_crops]
365
+
366
+ # If no seasonal match, use district preferences with seasonal weighting
367
+ if not suitable_crops:
368
+ if current_season == 'kharif':
369
+ # Monsoon crops preference
370
+ kharif_crops = ['rice', 'maize', 'arhar', 'moong', 'urd', 'jowar', 'bajra', 'groundnut', 'soybean']
371
+ suitable_crops = [crop for crop in all_district_crops if crop in kharif_crops]
372
+ elif current_season == 'rabi':
373
+ # Winter crops preference
374
+ rabi_crops = ['wheat', 'barley', 'gram', 'lentil', 'pea', 'mustard', 'linseed', 'potato']
375
+ suitable_crops = [crop for crop in all_district_crops if crop in rabi_crops]
376
+ elif current_season == 'zaid':
377
+ # Summer crops preference
378
+ zaid_crops = ['maize', 'moong', 'urd', 'watermelon', 'cucumber']
379
+ suitable_crops = [crop for crop in all_district_crops if crop in zaid_crops]
380
+
381
+ # If still no match, fall back to district primary crops
382
+ if not suitable_crops:
383
+ suitable_crops = district_prefs.get('primary', ['rice'])
384
+
385
+ # Weight selection based on crop category (primary crops more likely)
386
+ weighted_crops = []
387
+ for crop in suitable_crops:
388
+ if crop in district_prefs.get('primary', []):
389
+ weighted_crops.extend([crop] * 5) # 5x weight for primary crops
390
+ elif crop in district_prefs.get('secondary', []):
391
+ weighted_crops.extend([crop] * 3) # 3x weight for secondary crops
392
+ else:
393
+ weighted_crops.extend([crop] * 1) # 1x weight for specialty crops
394
+
395
+ selected_crop = random.choice(weighted_crops) if weighted_crops else 'rice'
396
+
397
+ logger.info(f"Selected crop: {selected_crop} for {district} in {current_season} season from options: {suitable_crops}")
398
+
399
+ return selected_crop
400
+
401
+
402
+ async def get_current_crop_stage_dynamic(crop: str, district: str = None):
403
+ """Determine crop stage based on current date and crop calendar - now more accurate"""
404
+
405
+ try:
406
+ # Get crop calendar information
407
+ crop_info = await crop_calendar_tools.get_crop_calendar('bihar', crop)
408
+
409
+ if "error" in crop_info:
410
+ # Fallback to the old static method
411
+ return get_current_crop_stage_static(crop)
412
+
413
+ # Parse planting and harvesting periods
414
+ planting_period = crop_info.get('planting', '')
415
+ season = crop_info.get('season', '')
416
+ stages = crop_info.get('stages', [])
417
+
418
+ current_month = datetime.now().month
419
+ current_date = date.today()
420
+
421
+ # Estimate planting date based on season and current month
422
+ estimated_plant_date = estimate_planting_date(crop, season, planting_period, current_month)
423
+
424
+ if estimated_plant_date:
425
+ # Use the crop calendar function to estimate stage
426
+ try:
427
+ stage_data = await crop_calendar_tools.estimate_crop_stage(
428
+ crop,
429
+ estimated_plant_date.isoformat(),
430
+ current_date.isoformat()
431
+ )
432
+
433
+ if "error" not in stage_data:
434
+ stage = stage_data.get('stage', stages[0] if stages else 'Growing')
435
+ logger.info(f"Dynamic stage calculation for {crop}: {stage} (planted ~{estimated_plant_date})")
436
+ return stage
437
+ except Exception as e:
438
+ logger.warning(f"Error in dynamic stage calculation: {e}")
439
+
440
+ # Fallback to month-based estimation
441
+ return estimate_stage_by_month(crop, current_month, stages)
442
+
443
+ except Exception as e:
444
+ logger.error(f"Error in dynamic crop stage calculation: {e}")
445
+ return get_current_crop_stage_static(crop)
446
+
447
+
448
+ def get_current_season(month: int):
449
+ """Determine current agricultural season"""
450
+ if month in [6, 7, 8, 9]: # June to September
451
+ return 'kharif'
452
+ elif month in [10, 11, 12, 1, 2, 3]: # October to March
453
+ return 'rabi'
454
+ else: # April, May
455
+ return 'zaid'
456
+
457
+
458
+ def estimate_planting_date(crop: str, season: str, planting_period: str, current_month: int):
459
+ """Estimate when the crop was likely planted based on season and current month"""
460
+ from datetime import date, timedelta
461
+
462
+ current_year = datetime.now().year
463
+
464
+ try:
465
+ if 'june-july' in planting_period.lower() or 'june' in planting_period.lower():
466
+ if current_month >= 6:
467
+ return date(current_year, 6, 15) # Mid June this year
468
+ else:
469
+ return date(current_year - 1, 6, 15) # Mid June last year
470
+
471
+ elif 'november-december' in planting_period.lower() or 'november' in planting_period.lower():
472
+ if current_month >= 11:
473
+ return date(current_year, 11, 15) # Mid November this year
474
+ elif current_month <= 4:
475
+ return date(current_year - 1, 11, 15) # Mid November last year
476
+ else:
477
+ return date(current_year, 11, 15) # Will be planted this November
478
+
479
+ elif 'october-november' in planting_period.lower() or 'october' in planting_period.lower():
480
+ if current_month >= 10:
481
+ return date(current_year, 10, 15)
482
+ elif current_month <= 4:
483
+ return date(current_year - 1, 10, 15)
484
+ else:
485
+ return date(current_year, 10, 15)
486
+
487
+ elif 'march-april' in planting_period.lower() or 'march' in planting_period.lower():
488
+ if current_month >= 3 and current_month <= 8:
489
+ return date(current_year, 3, 15)
490
+ else:
491
+ return date(current_year - 1, 3, 15)
492
+
493
+ except Exception as e:
494
+ logger.warning(f"Error estimating planting date: {e}")
495
+
496
+ return None
497
+
498
+
499
+ def estimate_stage_by_month(crop: str, current_month: int, stages: list):
500
+ """Estimate crop stage based on current month and crop type"""
501
+
502
+ if not stages:
503
+ return 'Growing'
504
+
505
+ # Month-based stage mapping for common crops
506
+ stage_mappings = {
507
+ 'rice': {
508
+ 6: 0, 7: 1, 8: 2, 9: 3, 10: 4, 11: 5, 12: 6, 1: 7, 2: 8, 3: 8, 4: 8, 5: 8
509
+ },
510
+ 'wheat': {
511
+ 11: 0, 12: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6, 6: 7, 7: 8, 8: 8, 9: 8, 10: 8
512
+ },
513
+ 'maize': {
514
+ 6: 0, 7: 1, 8: 2, 9: 3, 10: 4, 11: 5, 12: 6, 1: 7, 2: 7, 3: 0, 4: 1, 5: 2 # Dual season
515
+ }
516
+ }
517
+
518
+ crop_mapping = stage_mappings.get(crop, {})
519
+ stage_index = crop_mapping.get(current_month, 2) # Default to middle stage
520
+ stage_index = min(stage_index, len(stages) - 1)
521
+
522
+ return stages[stage_index] if stage_index < len(stages) else stages[-1]
523
+
524
+
525
+ def get_current_crop_stage_static(crop: str):
526
+ """Original static crop stage function as fallback"""
527
+ current_month = datetime.now().month
528
+
529
+ if crop == 'rice':
530
+ if current_month in [6, 7]:
531
+ return 'Transplanting'
532
+ elif current_month in [8, 9]:
533
+ return 'Vegetative'
534
+ elif current_month in [10, 11]:
535
+ return 'Flowering'
536
+ else:
537
+ return 'Maturity'
538
+ elif crop == 'wheat':
539
+ if current_month in [11, 12]:
540
+ return 'Sowing'
541
+ elif current_month in [1, 2]:
542
+ return 'Tillering'
543
+ elif current_month in [3, 4]:
544
+ return 'Flowering'
545
+ else:
546
+ return 'Harvesting'
547
+ elif crop == 'sugarcane':
548
+ if current_month in [2, 3, 4]:
549
+ return 'Planting'
550
+ elif current_month in [5, 6, 7, 8]:
551
+ return 'Vegetative'
552
+ elif current_month in [9, 10, 11]:
553
+ return 'Maturity'
554
+ else:
555
+ return 'Harvesting'
556
+ elif crop == 'maize':
557
+ if current_month in [6, 7]:
558
+ return 'Sowing'
559
+ elif current_month in [8, 9]:
560
+ return 'Vegetative'
561
+ elif current_month in [10, 11]:
562
+ return 'Grain Filling'
563
+ else:
564
+ return 'Harvesting'
565
+
566
+ return 'Growing'
567
+
568
+
569
  @app.get("/")
570
  async def root():
571
  return {"message": "MCP Weather Server is running"}