Spaces:
Sleeping
Sleeping
File size: 99,098 Bytes
12e0bb3 |
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 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 |
import os
import json
import uuid
from datetime import datetime, date, timedelta
from pathlib import Path
from flask import Flask, render_template, request, redirect, url_for, jsonify
# Telegram functionality has been removed/disabled.
# The original code dynamically imported `telegram_bot.py` and provided many helpers.
# To remove that dependency while keeping the app runnable, we set all telegram-related
# symbols to None or harmless defaults and provide a tiny stub for `ensure_telegram_loaded`.
start_telegram_bot = None
stop_telegram_bot = None
send_message = None
add_scheduled_item = None
load_telegram_data = None
save_telegram_data = None
UPDATE_QUESTIONS = {}
get_latest_farmer_updates = None
def ensure_telegram_loaded():
"""Telegram features intentionally disabled.
Returns (ok: bool, missing: list, error: str)
"""
return False, ['telegram_disabled'], 'Telegram support removed from this codebase'
# Import the Google Generative AI library
import google.generativeai as genai
# --- Configuration for Gemini API ---
# WARNING: Use environment variables in production.
# For local testing, you can hardcode, but it's NOT recommended for deployment.
GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY", "AIzaSyBYU1pjQW_tSSC07FOjUVWoAx-JbGvbQGE") # Placeholder, replace with your actual key or env var
if GOOGLE_API_KEY:
genai.configure(api_key=GOOGLE_API_KEY)
else:
print("WARNING: GOOGLE_API_KEY environment variable not set. Gemini API calls will fail.")
print("Please set it (e.g., export GOOGLE_API_KEY='YOUR_API_KEY' in your terminal before running app.py)")
# Choose the Gemini model
GEMINI_MODEL = 'gemini-1.5-flash' # Using Gemini 1.5 Flash
BASE_DIR = Path(__file__).resolve().parent
DATA_FILE = BASE_DIR / 'data' / 'farmers.json'
# New directory for saving generated AI output
GEN_AI_OUTPUT_DIR = BASE_DIR / 'data' / 'gen_ai_output'
app = Flask(__name__)
def load_data():
DATA_FILE.parent.mkdir(parents=True, exist_ok=True)
if not DATA_FILE.exists():
DATA_FILE.write_text('[]', encoding='utf-8')
try:
text = DATA_FILE.read_text(encoding='utf-8').strip()
if not text:
return []
return json.loads(text)
except Exception as e:
print(f"Error loading data: {e}")
return []
def get_record_by_id(record_id):
"""Helper to find a record by its ID."""
records = load_data()
for record in records:
if record.get('id') == record_id:
return record
return None
def save_record(record: dict):
records = load_data()
records.append(record)
DATA_FILE.write_text(json.dumps(records, indent=2, ensure_ascii=False), encoding='utf-8')
def clean_ai_response(response_text):
"""Clean markdown code blocks from the AI response."""
response_text = response_text.strip()
# Remove markdown code blocks
if response_text.startswith('```json') and response_text.endswith('```'):
response_text = response_text[7:-3].strip()
elif response_text.startswith('```') and response_text.endswith('```'):
response_text = response_text[3:-3].strip()
return response_text
def create_insights_prompt(record):
"""Create an enhanced, context-aware prompt for AI insights analysis."""
# Analyze farm type to customize recommendations
farming_type = record.get('farming_type', 'Mixed Farming')
# Calculate key metrics
total_crops = len(record.get('crops', [])) + len(record.get('horti_crops', []))
# Create context-specific guidance
context_guidance = {
'Crop Farming': "Focus on crop rotation, soil health, irrigation efficiency, and market timing.",
'Dairy Farming': "Emphasize milk yield optimization, animal welfare, fodder management, and value addition.",
'Poultry Farming': "Highlight biosecurity, feed conversion ratios, housing optimization, and disease prevention.",
'Plantation Farming': "Address long-term sustainability, processing opportunities, and climate adaptation.",
'Mixed Farming': "Balance resource allocation across enterprises and explore synergies between different farming activities."
}
guidance = context_guidance.get(farming_type, context_guidance['Mixed Farming'])
# Prepare JSON snippets safely from Python (avoid embedding Jinja-like tags inside f-strings)
irrigation_list = record.get('irrigation_infrastructure', []) or []
irrigation_labels_json = json.dumps(irrigation_list, ensure_ascii=False)
irrigation_values_json = json.dumps([1 for _ in irrigation_list])
# Conditional crop chart block (only include if crop farming and crops exist)
crop_chart_block = ''
if record.get('farming_type') == 'Crop Farming' and record.get('crops'):
crop_labels_list = [c.get('primary_crop') for c in record.get('crops', []) if c.get('primary_crop')]
crop_labels_json = json.dumps(crop_labels_list, ensure_ascii=False)
crop_values_json = json.dumps([1 for _ in crop_labels_list])
crop_chart_block = f',\n {{\n "chart_title": "Primary Crops Cultivated",\n "chart_type": "bar",\n "data_labels": {crop_labels_json},\n "data_values": {crop_values_json},\n "chart_description": "A bar chart illustrating the primary crops being cultivated on the farm."\n }}'
# Determine primary income source
primary_income_source = "Diverse sources"
if farming_type == 'Dairy Farming':
primary_income_source = "Dairy products"
elif farming_type == 'Crop Farming' and record.get('crops'):
# Safely get primary_crop from the first crop if available
primary_income_source = record['crops'][0].get('primary_crop', 'Various crops') if record['crops'] else 'Various crops'
elif farming_type == 'Poultry Farming':
primary_income_source = "Poultry and eggs"
elif farming_type == 'Plantation Farming' and record.get('plantation_type'):
primary_income_source = record['plantation_type'][0] if record['plantation_type'] else 'Various plantations' # First plantation type
prompt = f"""
You are Dr. Agricultural Expert, a seasoned farm consultant with 20+ years of experience across diverse farming systems in India.
Analyze this farm record with precision and provide actionable insights that can directly improve the farmer's livelihood.
**CRITICAL INSTRUCTIONS:**
- Output ONLY valid JSON with no markdown formatting.
- Be specific with numbers, percentages, and measurable outcomes.
- Consider local Indian agricultural practices and market conditions.
- {guidance}.
- All financial figures should be in Indian Rupees (βΉ).
- If a specific piece of data for a section is not available in the farm record, generate a plausible estimate or a relevant general statement, making it clear it's an estimate.
**REQUIRED JSON STRUCTURE:**
{{
"summary": "Professional 2-3 line assessment of farm status, key strengths, and primary focus areas",
"key_metrics": {{
"farm_size": "{record.get('area', 'Not specified')}",
"farming_type": "{farming_type}",
"soil_ph_status": "{record.get('ph_category', 'Not tested')}",
"organic_matter_level": "{record.get('organic_matter_level', 'Unknown')}",
"primary_irrigation": "{record.get('irrigation_source', 'Not specified')}",
"crop_diversity_score": {total_crops},
"water_quality_rating": "{record.get('water_quality', 'Not tested')}",
"production_focus": "{primary_income_source}"
}},
"recommendations": [
"HIGH PRIORITY: Specific action with expected 15-25% improvement in [metric] (e.g., 'Implement drip irrigation for main crop to reduce water usage by 20% and increase yield by 15%.')",
"MEDIUM PRIORITY: Technical upgrade with ROI timeline of [X] months (e.g., 'Invest in a small solar pump for existing borewell; estimated ROI 18 months through electricity savings.')",
"ONGOING: Best practice with seasonal implementation schedule (e.g., 'Regularly conduct soil moisture checks (weekly during dry season) to optimize irrigation timings.')",
"STRATEGIC: Long-term diversification opportunity with risk assessment (e.g., 'Explore cultivating high-value medicinal plants on 0.5 ha, mitigating market price volatility for primary crops.')"
],
"risk_factors": [
"IMMEDIATE: Critical vulnerability requiring urgent attention (e.g., 'Over-reliance on monsoon for Kharif crops poses high risk of failure during erratic rainfall periods.')",
"SEASONAL: Weather/market risks with mitigation strategies (e.g., 'Low market prices post-harvest for tomatoes; mitigate by exploring local processing units or cold storage options.')",
"SYSTEMIC: Long-term threats to farm sustainability (e.g., 'Continuous decline in groundwater levels due to borewell over-extraction; necessitates long-term water conservation plans.')"
],
"growth_opportunities": [
"Value addition opportunity with estimated revenue increase of X% (e.g., 'Processing surplus milk into paneer and ghee can increase dairy revenue by 20-30%.')",
"Technology adoption with specific ROI projections (e.g., 'Implementing an IoT-based weather station for precise spray schedules, reducing pesticide costs by 10% annually.')",
"Market linkage development with implementation roadmap (e.g., 'Establish direct sales channel to local urban consumers for fresh vegetables; target initial 10% sales increase in 6 months.')",
"Skill development area with training recommendations (e.g., 'Training in advanced composting techniques to improve organic matter and reduce chemical fertilizer dependence.')."
],
"financial_overview": {{
"estimated_monthly_costs": "βΉ[X] - βΉ[Y] based on farm scale and operations (e.g., 'βΉ15,000 - βΉ20,000 for fodder, labor, electricity for a typical 2ha farm.').",
"potential_monthly_income": "βΉ[X] - βΉ[Y] with current practices + improvements (e.g., 'βΉ30,000 - βΉ45,000, factoring in current production and potential gains.').",
"profit_margin_range": "[X]% - [Y]% depending on market conditions (e.g., '25% - 40% margin for dairy, highly sensitive to feed prices.').",
"cost_optimization": [
"Reduce [specific cost] by βΉ[amount] through [method] (e.g., 'Reduce electricity cost for irrigation by βΉ2,500/month by installing energy-efficient pumps.')",
"Optimize [resource] usage to save βΉ[amount] annually (e.g., 'Optimize fertilizer application based on soil test to save βΉ8,000 annually.')."
],
"revenue_enhancement": [
"Increase [product] price by βΉ[amount]/unit through [quality improvement] (e.g., 'Increase milk price by βΉ2/L by ensuring organic certification.').",
"Add [value-added product] for additional βΉ[amount]/month (e.g., 'Introduce packaged organic vegetables for additional βΉ10,000/month.')."
]
}},
"environmental_sustainability": {{
"carbon_footprint_assessment": "Current emissions level and reduction potential (e.g., 'Moderate carbon footprint from livestock methane and diesel generator usage; 10% reduction possible with manure management.').",
"soil_health_status": "Assessment based on organic matter and pH data (e.g., 'Soil is Neutral with Medium organic matter, good base; focus on increasing organic carbon content.').",
"water_conservation_potential": "Specific water saving opportunities with quantities (e.g., 'Potential to save 50,000 liters/season by converting flood irrigation to drip for 0.5 ha.').",
"biodiversity_enhancement": [
"Plant [specific trees/crops] for ecosystem services (e.g., 'Plant fruit trees (Guava, Mango) along farm borders to attract pollinators and provide additional income.').",
"Implement [specific practice] for beneficial insect habitat (e.g., 'Establish flowering strips near crops to attract beneficial insects for natural pest control.')."
],
"climate_resilience": [
"Adapt to temperature changes through [specific method] (e.g., 'Install shade nets for high-value horticulture crops during peak summer to prevent heat stress.').",
"Prepare for rainfall variability with [specific strategy] (e.g., 'Cultivate drought-resistant varieties of maize during erratic monsoon periods.')."
]
}},
"technology_integration": [
"IoT sensors for [specific application] - Expected savings: βΉ[amount]/year (e.g., 'IoT soil moisture sensors for precise irrigation scheduling - Expected savings: βΉ5,000/year in water and electricity.').",
"Mobile app for [specific function] - Efficiency gain: [X]% (e.g., 'Farm management mobile app for daily task assignment and monitoring - Efficiency gain: 10% in labor management.').",
"Mechanization of [specific process] - Labor cost reduction: [X]% (e.g., 'Small-scale inter-cultivator for weeding - Labor cost reduction: 15% for 1 ha.').",
"Digital marketing platform - Market reach increase: [X]% (e.g., 'Local e-commerce platform for direct farm sales - Market reach increase: 50% in nearby towns.')."
],
"benchmarking_analysis": {{
"regional_performance": "Compare farm metrics with district/state averages (e.g., 'Farm milk yield (12 L/day) is 20% above regional average (10 L/day).').",
"best_practice_gaps": "Identify specific areas lagging behind top performers (e.g., 'Crop rotation diversity is low compared to top-performing mixed farms in the region.').",
"competitive_advantages": "Highlight farm's unique strengths vs neighbors (e.g., 'Superior soil organic matter content and good water quality are key advantages.').",
"improvement_potential": "Quantify gaps and improvement possibilities (e.g., 'Yield gap for wheat is 20% compared to high-yield farms, achievable with improved fertilization.')."
}},
"future_projections": {{
"year_1_targets": "Specific, measurable goals for next 12 months (e.g., 'Increase average milk yield by 5% and reduce input costs by 8%.').",
"year_3_vision": "Medium-term transformation objectives (e.g., 'Establish a small value-addition unit for dairy products and achieve 50% water self-sufficiency.').",
"year_5_potential": "Long-term growth and sustainability scenario (e.g., 'Become a certified organic farm with diversified income streams from crops, dairy, and agri-tourism.').",
"market_trend_impact": "How emerging trends will affect this farm (e.g., 'Growing consumer demand for organic and locally sourced products will favor this farm if certified.').",
"climate_adaptation_timeline": "Phased approach to climate resilience (e.g., 'Phase 1: Drought-resistant crop selection (0-12 months); Phase 2: Rainwater harvesting & micro-irrigation expansion (1-3 years).')."
}},
"action_plan": {{
"short_term (0-6 months)": [
"Specific action 1 (e.g., 'Conduct detailed soil test for specific nutrient deficiencies and revise fertilization plan.').",
"Specific action 2 (e.g., 'Research and identify government subsidies available for solar pumps or water conservation.')."
],
"medium_term (6-18 months)": [
"Specific action 1 (e.g., 'Pilot value-added dairy products like yogurt or paneer to increase profit margins.').",
"Specific action 2 (e.g., 'Install IoT sensors for real-time monitoring of soil moisture and borewell levels.')."
],
"long_term (2-5 years)": [
"Specific action 1 (e.g., 'Invest in a biogas digester using cow dung to produce renewable energy and organic fertilizer.').",
"Specific action 2 (e.g., 'Develop direct-to-consumer marketing channels for milk and value-added products to capture higher margins.')."
]
}},
"resource_requirements": {{
"financial_planning": {{
"immediate_investment_needed": "βΉ[amount] for critical improvements (e.g., 'βΉ50,000 for drip irrigation system for 0.5 ha.').",
"annual_investment_budget": "βΉ[amount] for continuous improvement (e.g., 'βΉ30,000 annually for advanced seeds, organic inputs, and minor equipment upgrades.').",
"potential_loan_requirements": "βΉ[amount] with [tenure] repayment plan (e.g., 'βΉ2,00,000 loan for solar pump installation, 5-year repayment plan.').",
"subsidy_opportunities": ["Scheme 1: βΉ[amount] (e.g., 'National Horticulture Mission subsidy for cold storage: βΉ75,000').", "Scheme 2: βΉ[amount] (e.g., 'Pradhan Mantri Krishi Sinchayee Yojana for micro-irrigation: βΉ25,000')."]
}},
"knowledge_development": [
"[Specific skill] training - Duration: [hours] - Provider: [institution] (e.g., 'Organic farming certification course - Duration: 40 hours - Provider: Local Krishi Vigyan Kendra.').",
"[Technical knowledge] certification - Timeline: [months] - Cost: βΉ[amount] (e.g., 'Dairy farm management certification - Timeline: 3 months - Cost: βΉ10,000.')."
],
"infrastructure_upgrades": [
"[Specific upgrade] - Cost: βΉ[amount] - Installation time: [duration] (e.g., 'New cattle shed expansion - Cost: βΉ1,50,000 - Installation time: 2 months.').",
"[Equipment purchase] - Investment: βΉ[amount] - Payback period: [months] (e.g., 'Mini tractor purchase - Investment: βΉ5,00,000 - Payback period: 36 months.')."
]
}},
"data_visualizations": [
{{
"chart_title": "Production Efficiency Analysis",
"chart_type": "radar",
"chart_description": "Multi-dimensional farm performance assessment comparing current (green) vs target (blue) scores across key areas (max score 10).",
"data_labels": ["Soil Health", "Water Management", "Crop Productivity", "Market Access", "Technology Use", "Sustainability"],
"current_scores": [
{5 if record.get('organic_matter_level') == 'Low (1β2%)' else (7 if record.get('organic_matter_level') == 'Medium (2β4%)' else 9)},
{6 if record.get('irrigation_source') == 'Rainfed only' else (8 if 'Drip' in irrigation_list else 7)},
{6 if total_crops < 2 else (8 if total_crops >=2 else 5)},
{5 if record.get('dairy_market_access') == 'Direct sale' else (7 if record.get('dairy_market_access') == 'Milk cooperative' else 5)},
4,
{6 if 'Rainwater harvesting tank' in irrigation_list else 5}
],
"target_scores": [8, 9, 8, 8, 7, 8], # Example target scores
"max_score": 10
}},
{{
"chart_title": "Monthly Income Projection",
"chart_type": "line",
"chart_description": "Expected income growth over 12 months with recommended improvements (current vs. projected). Figures are illustrative.",
"data_labels": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
"current_income": [
{15000 if farming_type == 'Dairy Farming' else 10000}, {15000 if farming_type == 'Dairy Farming' else 10000},
{20000 if farming_type == 'Dairy Farming' else 15000}, {25000 if farming_type == 'Dairy Farming' else 20000},
{30000 if farming_type == 'Dairy Farming' else 25000}, {28000 if farming_type == 'Dairy Farming' else 22000},
{26000 if farming_type == 'Dairy Farming' else 20000}, {24000 if farming_type == 'Dairy Farming' else 18000},
{22000 if farming_type == 'Dairy Farming' else 16000}, {18000 if farming_type == 'Dairy Farming' else 14000},
{16000 if farming_type == 'Dairy Farming' else 12000}, {15000 if farming_type == 'Dairy Farming' else 10000}
],
"projected_income": [
{16000 if farming_type == 'Dairy Farming' else 12000}, {17000 if farming_type == 'Dairy Farming' else 13000},
{22000 if farming_type == 'Dairy Farming' else 18000}, {28000 if farming_type == 'Dairy Farming' else 24000},
{35000 if farming_type == 'Dairy Farming' else 30000}, {33000 if farming_type == 'Dairy Farming' else 28000},
{31000 if farming_type == 'Dairy Farming' else 26000}, {29000 if farming_type == 'Dairy Farming' else 24000},
{27000 if farming_type == 'Dairy Farming' else 22000}, {23000 if farming_type == 'Dairy Farming' else 19000},
{20000 if farming_type == 'Dairy Farming' else 16000}, {18000 if farming_type == 'Dairy Farming' else 14000}
],
"improvement_percentage": 15
}},
{{
"chart_title": "Resource Optimization Opportunities",
"chart_type": "doughnut",
"chart_description": "Potential annual cost savings (βΉ) by optimizing various farm resources. Figures are illustrative.",
"data_labels": ["Water Conservation", "Energy Efficiency", "Input Optimization", "Labor Efficiency", "Technology Adoption"],
"savings_potential": [5000, 3000, 4000, 2000, 1000], # Example savings
"total_annual_savings": 15000
}}
],
"expert_commentary": "Write a detailed 3-4 paragraph professional assessment as if consulting the farmer directly. Include specific observations about their current practices, highlight both strengths and improvement areas, provide confidence in achievable outcomes, and motivate with realistic success scenarios. Use encouraging but realistic language that builds trust and commitment to implementation."
}}
**FARM RECORD FOR ANALYSIS:**
{json.dumps(record, indent=2, ensure_ascii=False)}
**ANALYSIS REQUIREMENTS:**
1. Base all recommendations on actual data provided.
2. Consider {farming_type} best practices and economics.
3. Factor in Indian agricultural context and market conditions.
4. Provide specific, measurable, and time-bound suggestions.
5. Include realistic cost-benefit analysis for all recommendations.
6. Ensure all financial figures are in Indian Rupees (βΉ).
7. Consider seasonal variations and monsoon patterns.
8. Account for government schemes and subsidies available to farmers.
9. For sections where direct numerical data is missing (e.g., financial figures not in input record), generate plausible estimates and state they are estimates.
Generate comprehensive insights now:"""
return prompt
def create_yearly_plan_prompt(record):
"""Create a prompt for AI to generate a detailed yearly plan, tailored by farming type."""
current_year = datetime.now().year
next_year = current_year + 1
farming_type = record.get('farming_type', 'Mixed Farming')
# Normalize preferred language: accept string or list, take first entry before any '/'
raw_lang = record.get('preferred_lang_time', 'English')
try:
if isinstance(raw_lang, list) and raw_lang:
language = str(raw_lang[0]).split('/')[0].strip() or 'English'
else:
language = str(raw_lang).split('/')[0].strip() or 'English'
except Exception:
language = 'English'
# --- Parse and normalize farm area ---
farm_area_str = record.get('area', '0 ha').lower().replace('sqm', 'sq m').replace('sqkm', 'sq km')
farm_area_value = 0.0
farm_area_unit = 'ha'
if 'ha' in farm_area_str:
try:
farm_area_value = float(farm_area_str.replace('ha', '').strip())
farm_area_unit = 'ha'
except ValueError:
pass
elif 'sq m' in farm_area_str:
try:
farm_area_value = float(farm_area_str.replace('sq m', '').strip()) / 10000.0 # Convert sqm to ha
farm_area_unit = 'ha'
except ValueError:
pass
elif 'acres' in farm_area_str: # Assuming 1 acre = 0.404686 ha
try:
farm_area_value = float(farm_area_str.replace('acres', '').strip()) * 0.404686
farm_area_unit = 'ha'
except ValueError:
pass
# Provide a reasonable default if parsing fails or value is zero
if farm_area_value <= 0:
farm_area_value = 1.0 # Default to 1 ha for planning examples
farm_area_unit = 'ha'
# --- Dynamic JSON content blocks for each section based on farming_type ---
# --- Annual Calendar Content ---
annual_calendar_data = []
if farming_type == 'Dairy Farming':
annual_calendar_data = [
{"plot_name": f"Pasture/Fodder Field A (e.g., {farm_area_value/2:.1f} {farm_area_unit})", "crop": "Lucerne/Alfalfa", "timeline": [{"stage": "Sowing", "start": f"Oct {next_year-1}", "end": f"Oct 15 {next_year-1}"}, {"stage": "Harvest 1", "start": f"Dec 1 {next_year-1}", "end": f"Dec 10 {next_year-1}"}, {"stage": "Harvest 2", "start": f"Feb 1 {next_year}", "end": f"Feb 10 {next_year}"}, {"stage": "Harvest 3", "start": f"Apr 1 {next_year}", "end": f"Apr 10 {next_year}"}, {"stage": "Fallow/Summer Fodder", "start": f"May 1 {next_year}", "end": f"Sep 30 {next_year}"}]},
{"plot_name": f"Fodder Field B (e.g., {farm_area_value/2:.1f} {farm_area_unit})", "crop": "Maize (Fodder)", "timeline": [{"stage": "Sowing", "start": f"Jun 15 {next_year}", "end": f"Jun 30 {next_year}"}, {"stage": "Harvest", "start": f"Sep 1 {next_year}", "end": f"Sep 15 {next_year}"}, {"stage": "Rabi Fodder", "start": f"Oct 15 {next_year}", "end": f"Mar 30 {next_year+1}"}]}
]
elif farming_type == 'Crop Farming':
annual_calendar_data = [
{"plot_name": f"Field A (e.g., {farm_area_value/2:.1f} {farm_area_unit})", "crop": "Paddy (Hybrid)", "timeline": [{"stage": "Land Preparation", "start": f"Jun 1 {next_year}", "end": f"Jun 15 {next_year}"}, {"stage": "Sowing/Transplanting", "start": f"Jun 16 {next_year}", "end": f"Jul 10 {next_year}"}, {"stage": "Harvest Window", "start": f"Sep 30 {next_year}", "end": f"Oct 30 {next_year}"}, {"stage": "Rabi Sowing (Wheat)", "start": f"Nov 1 {next_year}", "end": f"Nov 15 {next_year}"}]},
{"plot_name": f"Field B (e.g., {farm_area_value/2:.1f} {farm_area_unit})", "crop": "Wheat (High-Yielding)", "timeline": [{"stage": "Land Preparation", "start": f"Oct 15 {next_year}", "end": f"Oct 30 {next_year}"}, {"stage": "Sowing", "start": f"Nov 1 {next_year}", "end": f"Nov 15 {next_year}"}, {"stage": "Harvest Window", "start": f"Mar 15 {next_year+1}", "end": f"Apr 15 {next_year+1}"}]}
]
elif farming_type == 'Poultry Farming':
annual_calendar_data = [
{"cycle_name": "Broiler Batch 1", "timeline": [{"stage": "Chick Placement", "start": f"Jan 1 {next_year}", "end": f"Jan 5 {next_year}"}, {"stage": "Rearing", "start": f"Jan 6 {next_year}", "end": f"Feb 15 {next_year}"}, {"stage": "Harvest/Sale", "start": f"Feb 16 {next_year}", "end": f"Feb 28 {next_year}"}]},
{"cycle_name": "Layer Flock", "timeline": [{"stage": "Chick Arrival", "start": f"Mar 1 {next_year}", "end": f"Mar 5 {next_year}"}, {"stage": "Grower Period", "start": f"Mar 6 {next_year}", "end": f"Jul 30 {next_year}"}, {"stage": "Laying Period", "start": f"Aug 1 {next_year}", "end": f"Jul 30 {next_year+1}"}]}
]
elif farming_type == 'Horticulture Farming':
annual_calendar_data = [
{"plot_name": f"Polyhouse A (e.g., {farm_area_value/4:.1f} {farm_area_unit})", "crop": "Capsicum (Bell Pepper)", "timeline": [{"stage": "Nursery Prep", "start": f"Jan 1 {next_year}", "end": f"Jan 15 {next_year}"}, {"stage": "Transplanting", "start": f"Feb 1 {next_year}", "end": f"Feb 10 {next_year}"}, {"stage": "Harvesting (Continuous)", "start": f"Apr 1 {next_year}", "end": f"Sep 30 {next_year}"}]},
{"plot_name": f"Open Field B (e.g., {farm_area_value/2:.1f} {farm_area_unit})", "crop": "Tomato (Local)", "timeline": [{"stage": "Nursery Prep", "start": f"Jun 1 {next_year}", "end": f"Jun 15 {next_year}"}, {"stage": "Transplanting", "start": f"Jul 1 {next_year}", "end": f"Jul 10 {next_year}"}, {"stage": "Harvesting", "start": f"Oct 1 {next_year}", "end": f"Nov 15 {next_year}"}]}
]
elif farming_type == 'Plantation Farming':
annual_calendar_data = [
{"plantation_name": f"Tea Garden (e.g., {farm_area_value/2:.1f} {farm_area_unit})", "crop": "Assam Tea Clone", "timeline": [{"stage": "Pruning", "start": f"Dec {next_year-1}", "end": f"Jan {next_year}"}, {"stage": "First Flush Plucking", "start": f"Mar {next_year}", "end": f"Apr {next_year}"}, {"stage": "Second Flush Plucking", "start": f"May {next_year}", "end": f"Jun {next_year}"}, {"stage": "Monsoon Flush", "start": f"Jul {next_year}", "end": f"Sep {next_year}"}, {"stage": "Autumnal Flush", "start": f"Oct {next_year}", "end": f"Nov {next_year}"}]},
{"plantation_name": f"Coconut Grove (e.g., {farm_area_value/2:.1f} {farm_area_unit})", "crop": "West Coast Tall", "timeline": [{"stage": "Nut Harvesting", "start": f"Quarterly (Jan, Apr, Jul, Oct {next_year})", "end": "Continuous"}, {"stage": "Fertilization", "start": f"May {next_year}/Sep {next_year}"}]}
]
else: # Mixed Farming / Default
annual_calendar_data = [
{"plot_name": f"Main Field (e.g., {record.get('area', 'N/A')} ha)", "crop": "Mixed Kharif Crops (e.g., Maize & Pulses)", "timeline": [{"stage": "Sowing", "start": f"Jun 15 {next_year}", "end": f"Jul 10 {next_year}"}, {"stage": "Harvest", "start": f"Sep 30 {next_year}", "end": f"Oct 30 {next_year}"}]},
{"plot_name": "Animal Care Block", "crop": "Dairy Herd/Poultry Flock", "timeline": [{"stage": "Animal Health Check", "start": "Quarterly", "end": "Continuous"}, {"stage": "Fodder Sowing (Rabi)", "start": f"Oct 1 {next_year}", "end": f"Oct 15 {next_year}"}, {"stage": "Milk/Egg Production", "start": "Year-round", "end": "Continuous"}]}
]
annual_calendar_json = json.dumps(annual_calendar_data, ensure_ascii=False, indent=2)
# --- Irrigation Schedule Content ---
irrigation_schedule_data = []
if farming_type == 'Dairy Farming':
irrigation_schedule_data = [
{"month": f"Jan {next_year}", "recommendation": "Weekly irrigation for perennial fodder crops (e.g., Lucerne). Maintain soil moisture > 60% for pastures. Supplement water for animals."},
{"month": f"Jul {next_year}", "recommendation": "Monitor monsoon. Supplemental irrigation if deficit. Manage muddy areas around sheds. Implement pasture rotation if possible. Focus on quality green fodder."}
]
elif farming_type == 'Crop Farming':
irrigation_schedule_data = [
{"month": f"Jan {next_year}", "recommendation": "Weekly drip irrigation (60 min/cycle, 3 cycles/week) for Rabi crops; maintain soil moisture > 60%. Prioritize young plants."},
{"month": f"Jul {next_year}", "recommendation": "Monitor monsoon. Supplemental irrigation if rainfall deficit. Focus on even water distribution for Kharif crops."}
]
elif farming_type == 'Poultry Farming':
irrigation_schedule_data = [
{"month": "Year-round", "recommendation": "Ensure constant supply of clean, fresh drinking water for all birds. Maintain optimal humidity levels in sheds, especially during summer (foggers/sprinklers). Water for cleaning & sanitation (daily)."}
]
elif farming_type == 'Horticulture Farming':
irrigation_schedule_data = [
{"month": f"Jan-Mar {next_year}", "recommendation": "Daily drip irrigation for polyhouse crops (e.g., Capsicum) for precise water delivery. For open field, ensure consistent moisture for seedlings. Monitor soil moisture at 15cm depth."},
{"month": f"Jul-Sep {next_year}", "recommendation": "Adjust irrigation based on monsoon rainfall. Reduce during heavy rains to prevent waterlogging; increase during dry spells. Focus on fungal disease prevention due to humidity. Ensure good drainage."}
]
elif farming_type == 'Plantation Farming':
irrigation_schedule_data = [
{"month": f"Jan-Mar {next_year}", "recommendation": "Critical irrigation for tea during dry periods to ensure good flush. Drip irrigation for young coconut/arecanut plants (daily 20-30L/plant). Monitor soil moisture at 30-60cm depth."},
{"month": f"Jul-Sep {next_year}", "recommendation": "Primarily rain-fed during monsoon. Ensure excellent drainage to prevent root rot in coffee/rubber. Supplemental irrigation only during prolonged dry spells within monsoon."}
]
else: # Mixed Farming / Default
irrigation_schedule_data = [
{"month": f"Monsoon Season (Jun-Sep {next_year})", "recommendation": "Primarily rain-fed for Kharif crops. Supplemental irrigation for dry spells. Ensure drainage for fields and clean water for livestock/poultry. Monitor water quality."},
{"month": f"Dry Season (Oct-May {next_year})", "recommendation": "Focused irrigation for Rabi crops and perennial fodder/horticulture. Use drip/sprinkler systems. Ensure consistent drinking water for animals. Monitor borewell levels closely."}
]
irrigation_schedule_json = json.dumps(irrigation_schedule_data, ensure_ascii=False, indent=2)
# --- Nutrient & Fertilizer Schedule Content ---
nutrient_fertilizer_schedule_data = []
if farming_type == 'Dairy Farming':
nutrient_fertilizer_schedule_data = [
{"crop_stage": "Pasture Establishment/Preparation", "recommendation": "Basal application of 10-15 tonnes/ha FYM/compost. NPK 19:19:19 (50 kg/ha) at sowing for initial growth. Consider soil test based micro-nutrient application. (Local availability of NPK, Urea, DAP)."},
{"crop_stage": "Animal Nutrition (Year-round)", "recommendation": f"Ensure balanced feed ration based on milk yield ({record.get('dairy_avg_milk_production', 'N/A')} L/day) and animal age. Supplement with mineral mixtures and vitamins. Consider local availability of cotton seed cake, oil cakes, and hay/silage."}
]
elif farming_type == 'Crop Farming':
nutrient_fertilizer_schedule_data = [
{"crop_stage": "Land Preparation (all crops)", "recommendation": "Basal application of 10-15 tonnes/ha Farm Yard Manure (FYM) or compost. Consider soil pH adjustment. (Local availability of NPK 19:19:19, Urea, DAP)."},
{"crop_stage": "Vegetative Growth", "recommendation": "First top-dressing of Urea (40 kg/ha) at 20-30 DAP/DAT. Organic alternative: Liquid Jeevamrutha or vermicompost tea fortnightly."}
]
elif farming_type == 'Poultry Farming':
nutrient_fertilizer_schedule_data = [
{"crop_stage": "Broiler Chicks (0-3 weeks)", "recommendation": "Starter feed with 22-23% protein. Ensure continuous availability. (Local feed mills, branded feeds like Godrej, Suguna)."},
{"crop_stage": "Laying Hens", "recommendation": "Layer feed with 16-18% protein and adequate calcium for strong eggshells. Ensure grit availability. (Local feed mills, branded feeds)."}
]
elif farming_type == 'Horticulture Farming':
nutrient_fertilizer_schedule_data = [
{"crop_stage": "Transplanting", "recommendation": "Basal application of FYM (10 tonnes/ha) + DAP (40 kg/ha) + MOP (30 kg/ha). Apply bio-fertilizers (Azotobacter, PSB). For polyhouse, use fertigation (water-soluble NPK). "},
{"crop_stage": "Fruiting/Harvest", "recommendation": "High potassium fertilizer (NPK 0:0:50) via fertigation or soil application to enhance fruit quality and size. Continue micronutrient support."}
]
elif farming_type == 'Plantation Farming':
nutrient_fertilizer_schedule_data = [
{"crop_stage": "Tea (Post-Pruning/Flushes)", "recommendation": "Split application of NPK (e.g., 100:60:60 kg/ha/year). Urea after each flush, MOP/SSP during monsoon. Micronutrients (Zn, B) foliar spray. (Local availability of tea-specific fertilizers)."},
{"crop_stage": "Coconut/Arecanut (Annual)", "recommendation": "Annual application of NPKMg (e.g., 500g N, 300g P, 1.2kg K, 500g Mg per palm). Apply in two splits (May, Sep). Use organic manure (FYM 50kg/palm) annually."}
]
else: # Mixed Farming / Default
nutrient_fertilizer_schedule_data = [
{"crop_stage": "Overall Farm Soil Health", "recommendation": "Annual soil testing. Basal application of 10-15 tonnes/ha FYM/compost. Integrate crop residue and animal manure for nutrient cycling. (Local availability of NPK blends)."},
{"crop_stage": "Animal Feed", "recommendation": "Ensure balanced feed rations for dairy/poultry based on production stage. Supplement with minerals/vitamins. Source local feed ingredients for cost-effectiveness."}
]
nutrient_fertilizer_schedule_json = json.dumps(nutrient_fertilizer_schedule_data, ensure_ascii=False, indent=2)
# --- Pest & Disease Monitoring Plan Content ---
pest_disease_monitoring_plan_data = []
if farming_type == 'Dairy Farming':
pest_disease_monitoring_plan_data = [
{"month": f"Jan-Mar {next_year}", "target": "Mastitis (cows), External parasites (ticks, flies)", "monitoring": "Daily udder check, periodic body checks, fly traps, dung examination for worms.", "IPM_action": "Maintain udder hygiene, regular deworming, clean sheds, proper ventilation. Implement vaccination schedule.", "pesticide_name": "Anti-parasitic pour-on (vet prescribed)"},
{"month": f"Jul-Sep {next_year}", "target": "Internal parasites (worms), Monsoon-related infections (e.g., E.coli, foot rot)", "monitoring": "Fecal examination, observe animal droppings, check hooves regularly.", "IPM_action": "Strategic deworming, maintain dry bedding, prevent water stagnation, elevate feeding troughs.", "pesticide_name": "Anthelmintics (as prescribed by vet), topical antiseptics for wounds"}
]
elif farming_type == 'Crop Farming':
pest_disease_monitoring_plan_data = [
{"month": f"Jul-Aug {next_year}", "target": "Leaf folder (Paddy), Blast disease (Paddy), Downy Mildew (Maize)", "monitoring": "Scouting 2x/week, weather-based disease forecasting", "IPM_action": "Early detection & removal of infected plants, proper spacing.", "pesticide_name": "Tricyclazole (for Blast), Mancozeb (Downy Mildew)"},
{"month": f"Nov-Dec {next_year}", "target": "Whitefly (Mustard), Rust (Wheat)", "monitoring": "Sticky traps, regular plant checks", "IPM_action": "Use insect nets, resistant varieties.", "pesticide_name": "Propiconazole (for Rust)"}
]
elif farming_type == 'Poultry Farming':
pest_disease_monitoring_plan_data = [
{"month": "Year-round", "target": "Coccidiosis, Ranikhet (ND), Gumboro, Fowl Pox, external parasites (mites, lice)", "monitoring": "Daily bird observation (activity, feed intake, droppings), vaccination records, post-mortem for mortality causes.", "IPM_action": "Strict biosecurity, regular disinfection, proper litter management, controlled access to sheds. Implement complete vaccination schedule.", "pesticide_name": "Coccidiostats (in feed), specific vaccines, external parasiticides (vet recommended)"}
]
elif farming_type == 'Horticulture Farming':
pest_disease_monitoring_plan_data = [
{"month": f"Jan-Mar {next_year}", "target": "Thrips, Mites, Powdery Mildew (Capsicum); Damping-off (Nursery)", "monitoring": "Yellow/Blue sticky traps, daily plant underside checks, regular nursery inspection.", "IPM_action": "Neem oil spray (1%), proper ventilation in polyhouse, seed treatment with Trichoderma.", "pesticide_name": "Fenpropathrin (mites), Sulphur (Powdery Mildew)"},
{"month": f"Jul-Sep {next_year}", "target": "Fruit borer (Tomato), Early/Late Blight, Whitefly", "monitoring": "Pheromone traps, regular scouting for lesions/insects, weather-based alerts.", "IPM_action": "Trichogramma release, removal of infected leaves, proper spacing. Use resistant varieties.", "pesticide_name": "Chlorantraniliprole (borer), Mancozeb/Metalaxyl (blight)"}
]
elif farming_type == 'Plantation Farming':
pest_disease_monitoring_plan_data = [
{"month": f"Mar-May {next_year}", "target": "Tea Mosquito Bug (Tea), Mealybugs (Coffee), Red Palm Weevil (Coconut)", "monitoring": "Regular scouting for pest damage, pheromone traps, visual checks.", "IPM_action": "Pruning affected parts, biological control (e.g., parasitoids), use of neem-based pesticides.", "pesticide_name": "Monocrotophos (Tea), Chlorpyrifos (Coconut - trunk injection)"},
{"month": f"Jul-Sep {next_year}", "target": "Blister Blight (Tea), Coffee Berry Borer, Phytophthora (Arecanut)", "monitoring": "Disease incidence mapping, weather-based forecasting, fruit inspection.", "IPM_action": "Resistant varieties, proper drainage, copper fungicides (preventive).", "pesticide_name": "Hexaconazole (Blister Blight), Bordeaux Mixture (Phytophthora)"}
]
else: # Mixed Farming / Default
pest_disease_monitoring_plan_data = [
{"month": f"Monsoon (Jul-Sep {next_year})", "target": "Crop-specific pests (e.g., leaf folder), Animal diseases (e.g., FMD, monsoon infections)", "monitoring": "Weekly field scouting, daily animal observation, weather-based disease alerts.", "IPM_action": "Crop rotation, biopesticides, vaccination schedules for livestock, strict biosecurity for poultry.", "pesticide_name": "As per specific pest/disease (vet/agri expert advice)"},
{"month": f"Winter (Nov-Feb {next_year+1})", "target": "Rabi crop pests (e.g., aphids), Animal respiratory issues", "monitoring": "Sticky traps, daily animal health checks.", "IPM_action": "Resistant varieties, proper ventilation in animal sheds.", "pesticide_name": "As per specific pest/disease (vet/agri expert advice)"}
]
pest_disease_monitoring_plan_json = json.dumps(pest_disease_monitoring_plan_data, ensure_ascii=False, indent=2)
# --- Labour & Operations Calendar Content ---
labour_operations_calendar_data = []
if farming_type == 'Dairy Farming':
labour_operations_calendar_data = [
{"month": f"Jan {next_year}", "tasks": ["Fodder harvesting & feeding (daily)", "Daily milking & milk chilling", "Shed cleaning & bedding change", "Animal health monitoring"], "peak_demand": "Consistent"},
{"month": f"Oct {next_year}", "tasks": ["Rabi fodder sowing", "New animal procurement (if planned)", "Milking & herd management"], "peak_demand": "High"}
]
elif farming_type == 'Crop Farming':
labour_operations_calendar_data = [
{"month": f"Jun {next_year}", "tasks": ["Kharif land preparation & sowing", "Seed treatment"], "peak_demand": "High throughout"},
{"month": f"Oct {next_year}", "tasks": ["Rabi land preparation & sowing", "Irrigation setup"], "peak_demand": "High throughout"},
{"month": f"Mar {next_year+1}", "tasks": ["Rabi crop harvesting", "Threshing & drying"], "peak_demand": "High throughout"}
]
elif farming_type == 'Poultry Farming':
labour_operations_calendar_data = [
{"month": "Year-round (daily)", "tasks": ["Feeding & watering", "Egg collection (layers)", "Litter management & shed cleaning", "Bird health check & mortality removal"], "peak_demand": "Consistent"},
{"month": "As per batch", "tasks": ["Chick placement", "Vaccination drives", "Batch harvesting & dispatch"], "peak_demand": "Intermittent High"}
]
elif farming_type == 'Horticulture Farming':
labour_operations_calendar_data = [
{"month": f"Feb {next_year}", "tasks": ["Polyhouse transplanting (Capsicum)", "Open field nursery preparation", "Pruning & staking", "Daily harvesting (if continuous crop)"], "peak_demand": "High"},
{"month": f"Jul {next_year}", "tasks": ["Open field transplanting (Tomato)", "Weeding & intercultural operations", "Pest & disease management"], "peak_demand": "Mid-month"},
{"month": f"Oct {next_year}", "tasks": ["Open field harvesting (Tomato)", "Post-harvest handling (sorting, grading, packing)", "Field clearing"], "peak_demand": "High"}
]
elif farming_type == 'Plantation Farming':
labour_operations_calendar_data = [
{"month": f"Jan-Feb {next_year}", "tasks": ["Pruning (Tea/Coffee)", "Field clearing", "Weeding", "Fertilizer application"], "peak_demand": "High"},
{"month": f"Mar-Nov {next_year}", "tasks": ["Plucking/Harvesting (continuous for Tea/Coffee)", "Pest & disease management", "Irrigation management", "Weeding"], "peak_demand": "Consistent High"}
]
else: # Mixed Farming / Default
labour_operations_calendar_data = [
{"month": f"Jun-Jul {next_year}", "tasks": ["Kharif land preparation & sowing", "Animal shed cleaning", "Fodder cutting/feeding"], "peak_demand": "High for planting"},
{"month": f"Oct-Nov {next_year}", "tasks": ["Kharif harvest", "Rabi sowing", "Animal health check", "Input procurement"], "peak_demand": "High for harvest & planting"}
]
labour_operations_calendar_json = json.dumps(labour_operations_calendar_data, ensure_ascii=False, indent=2)
# --- Input Procurement & Budget Timeline Content ---
input_procurement_budget_timeline_data = []
if farming_type == 'Dairy Farming':
input_procurement_budget_timeline_data = [
{"item": "Fodder Seeds (Rabi)", "quantity": f"Varies by area ({record.get('area', 'N/A')})", "estimated_cost": "βΉ2,000 - βΉ5,000", "procurement_month": f"Sep-Oct {next_year-1}"},
{"item": "Animal Feed Concentrates", "quantity": f"Monthly requirement for {record.get('dairy_num_animals_cows', 0)} cows", "estimated_cost": "βΉ10,000 - βΉ15,000/month", "procurement_month": "Ongoing, bulk purchase quarterly"}
]
elif farming_type == 'Crop Farming':
input_procurement_budget_timeline_data = [
{"item": "Seeds (Kharif)", "quantity": "Varies by crop/area", "estimated_cost": "βΉ8,000 - βΉ15,000", "procurement_month": f"May-June {next_year}"},
{"item": "Fertilizers (Kharif Basal)", "quantity": "DAP 50kg, MOP 30kg/ha", "estimated_cost": "βΉ3,000 - βΉ5,000", "procurement_month": f"June {next_year}"},
{"item": "Seeds (Rabi)", "quantity": "Varies by crop/area", "estimated_cost": "βΉ7,000 - βΉ12,000", "procurement_month": f"Sep-Oct {next_year}"}
]
elif farming_type == 'Poultry Farming':
input_procurement_budget_timeline_data = [
{"item": "Chicks (Broiler/Layer)", "quantity": f"{record.get('poultry_num_birds', 0)} per batch/flock", "estimated_cost": "βΉ30-βΉ50/chick", "procurement_month": "As per batch cycle"},
{"item": "Poultry Feed", "quantity": "Approx. 120g/bird/day (layers), 4-5 kg/bird (broilers)", "estimated_cost": "βΉ35-βΉ45/kg", "procurement_month": "Monthly/Bulk"}
]
elif farming_type == 'Horticulture Farming':
input_procurement_budget_timeline_data = [
{"item": "Hybrid Seeds (Capsicum/Tomato)", "quantity": "As per area", "estimated_cost": "βΉ5,000 - βΉ10,000", "procurement_month": f"Dec {next_year-1}/May {next_year}"},
{"item": "Fertilizers (Water Soluble/Granular)", "quantity": "Monthly requirement", "estimated_cost": "βΉ3,000 - βΉ6,000/month", "procurement_month": "Ongoing"}
]
elif farming_type == 'Plantation Farming':
input_procurement_budget_timeline_data = [
{"item": "Fertilizers (NPK, Urea)", "quantity": "Annual bulk", "estimated_cost": "βΉ15,000 - βΉ30,000/ha", "procurement_month": f"Feb {next_year}, Aug {next_year}"},
{"item": "Labour Wages", "quantity": "Daily/Monthly", "estimated_cost": "βΉ20,000 - βΉ50,000/month", "procurement_month": "Ongoing"}
]
else: # Mixed Farming / Default
input_procurement_budget_timeline_data = [
{"item": "Seeds (Kharif/Rabi)", "quantity": "Varies by crop/area", "estimated_cost": "βΉ10,000 - βΉ20,000", "procurement_month": f"May-Jun {next_year}, Sep-Oct {next_year}"},
{"item": "Animal Feed/Supplements", "quantity": "Monthly requirement", "estimated_cost": "βΉ8,000 - βΉ15,000/month", "procurement_month": "Ongoing"}
]
input_procurement_budget_timeline_json = json.dumps(input_procurement_budget_timeline_data, ensure_ascii=False, indent=2)
# --- Market & Sales Guidance Content ---
market_sales_guidance_data = []
if farming_type == 'Dairy Farming':
market_sales_guidance_data = [
{"crop_product": "Dairy Milk", "guidance": f"Daily sales to milk cooperative ({record.get('dairy_market_access', 'local collection center')}). Explore direct sales to local consumers/sweet shops for 15-20% higher price, focusing on freshness and quality. Consider launching a small, local brand for {record.get('farm_name', 'your farm')} milk."},
{"crop_product": "Value-Added Dairy Products (Paneer, Ghee)", "guidance": "If processing capacity exists, target local grocery stores and online delivery platforms. Focus on product quality, consistent supply, and attractive packaging to build brand loyalty and increase revenue by 20-30%. Consider seasonal demand peaks."}
]
elif farming_type == 'Crop Farming':
market_sales_guidance_data = [
{"crop_product": "Kharif Crops (e.g., Paddy)", "guidance": f"Harvest in Sept-Oct {next_year}. Sell to government procurement agencies (FCI) or local mandis. Consider small-scale processing for value addition."},
{"crop_product": "Rabi Crops (e.g., Wheat)", "guidance": f"Harvest in March-April {next_year+1}. Target early market for better prices or store for 1-2 months for price stabilization. Explore local flour mills as buyers."}
]
elif farming_type == 'Poultry Farming':
market_sales_guidance_data = [
{"crop_product": "Eggs", "guidance": "Daily collection & grading. Sell to local retailers, restaurants, or directly to consumers. Explore bulk contracts with hotels/bakeries for stable pricing. Target fresh market for premium."},
{"crop_product": "Broilers", "guidance": "Harvest & sell at optimal weight (1.8-2.2 kg). Target local meat shops, restaurants, or direct sales. Monitor market prices closely for best returns."}
]
elif farming_type == 'Horticulture Farming':
market_sales_guidance_data = [
{"crop_product": "Capsicum (Polyhouse)", "guidance": "Continuous harvesting. Target premium urban markets, resorts, hotels. Focus on consistent quality, attractive packaging. Explore online B2C platforms for higher margins."},
{"crop_product": "Tomato (Open Field)", "guidance": "Harvest based on market demand. Sell to local mandis, aggregators. Consider small-scale processing (sauce, puree) for surplus/off-season sales. Stagger planting to avoid market glut."}
]
elif farming_type == 'Plantation Farming':
market_sales_guidance_data = [
{"crop_product": "Tea Leaves (Green/Made)", "guidance": "Sell to local tea factories or auction centers. Focus on consistent quality for better prices. Explore small-batch specialty tea production for premium markets."},
{"crop_product": "Coconuts/Arecanuts", "guidance": "Sell fresh nuts to local markets. For processing, sell to coir industries (husks), oil mills (copra), or local sweet shops. Value addition (e.g., virgin coconut oil)."}
]
else: # Mixed Farming / Default
market_sales_guidance_data = [
{"crop_product": "Crops (e.g., Maize, Pulses)", "guidance": "Sell harvested produce to local mandis. Explore government procurement schemes. Consider processing for value addition (e.g., maize flour). Stagger planting if possible."},
{"crop_product": "Milk/Eggs", "guidance": "Daily sales to cooperatives or local consumers. Focus on quality and freshness. Explore small-scale direct marketing in nearby towns."}
]
market_sales_guidance_json = json.dumps(market_sales_guidance_data, ensure_ascii=False, indent=2)
prompt = f"""
You are Dr. AgriPlan, a highly experienced agricultural planner and farm business strategist.
Your task is to generate a comprehensive, actionable, and detailed yearly operational plan for the farm based on the provided record.
The plan must be specifically and *exclusively* tailored to the farm's primary type: **{farming_type}**.
It should cover all critical aspects of farm management for the year {next_year}, with a strong focus on maximizing yield, optimizing costs, and ensuring sustainability within the Indian agricultural context.
**CRITICAL INSTRUCTIONS:**
- The output MUST be a JSON object with the following STRICT structure. Adhere to all keys, data types, and array formats precisely.
- All content (text, headings, recommendations, specific terms) should be generated in {language} language.
- Be highly specific, use quantifiable metrics (e.g., kg/ha, mm, βΉ amounts, percentages), and provide actionable steps.
- If specific numerical data is missing in the farm record for calculations (e.g., exact yields for financial projections), provide realistic, plausible estimates and explicitly state they are estimates.
- Assume typical local Indian conditions for the region around Latitude: {record.get('geo', {}).get('latitude', 'N/A')}, Longitude: {record.get('geo', {}).get('longitude', 'N/A')}.
- For all sections, ensure the content is *strictly relevant* to **{farming_type}**.
- If **{farming_type}** is NOT Crop Farming, do NOT include details about traditional field crops (paddy, wheat, tomato) in crop calendar, irrigation, fertilizer, pest, or market guidance. Instead, focus on feed/fodder, animal health, milk/egg sales, etc.
- If **{farming_type}** IS Crop Farming, focus primarily on field crops.
- If a section is genuinely not applicable, provide a very brief statement like "Not applicable as primary farming is {farming_type}." but try to find a relevant equivalent.
**REQUIRED JSON STRUCTURE:**
{{
"executive_summary": {{
"summary_paragraph": "A one-paragraph overview of the yearly plan, highlighting main goals and expected outcomes for the {next_year} farming season in {language}.",
"main_recommendation": "The single most impactful recommendation for the year in {language}."
}},
"annual_crop_calendar": {annual_calendar_json},
"irrigation_schedule": {irrigation_schedule_json},
"nutrient_fertilizer_schedule": {nutrient_fertilizer_schedule_json},
"pest_disease_monitoring_plan": {pest_disease_monitoring_plan_json},
"labour_operations_calendar": {labour_operations_calendar_json},
"input_procurement_budget_timeline": {input_procurement_budget_timeline_json},
"market_sales_guidance": {market_sales_guidance_json},
"sensors_monitoring_plan": [
{{"sensor": "Soil Moisture Sensors", "purpose": "Optimize irrigation", "frequency": "Daily readings", "alert_threshold": "Below 50% field capacity", "action": "Initiate drip irrigation"}},
{{"sensor": "Weather Station (Basic)", "purpose": "Pest/disease prediction, irrigation timing", "frequency": "Hourly data", "alert_threshold": "High humidity + specific temperature", "action": "Pre-emptive fungicide spray"}}
],
"risk_contingency_plan": [
{{"risk": "Drought/Water Scarcity", "strategy": "Alternate sowing of short-duration, drought-tolerant varieties (e.g., Bajra instead of Maize). Expand farm pond/rainwater harvesting capacity. Engage with water-sharing cooperative. Purchase water via tanker if critical."}},
{{"risk": "Excessive Rainfall/Flooding", "strategy": "Ensure proper field drainage. Raise seedbeds. Select flood-tolerant crop varieties for low-lying areas. Crop insurance for heavy rains."}}
],
"tasks_reminders": [
{{"task_id": "T001", "due_date": "{next_year}-06-01", "action": "Kharif Land Preparation / Fodder Sowing / Chick Placement", "priority": "High"}},
{{"task_id": "T002", "due_date": "{next_year}-10-05", "action": "Rabi Land Preparation / Fodder Sowing / Animal Health Check", "priority": "High"}}
],
"metrics_evaluation": [
"Yield Target: Increase primary product yield by 10-15% (e.g., Milk 14 L/day/cow, Wheat 40 qtl/ha, Eggs 300/bird/year).",
"Input Cost per Hectare/Animal/Bird: Reduce by 5-10% through optimization.",
"Profit per enterprise: Monitor and ensure minimum 30% margin for major activities.",
"Task Completion Rate: Target 90% completion of scheduled tasks."
],
"notes": "This plan is a dynamic document. Regular monitoring, feedback from field observations, and adaptation to market and weather conditions are essential for successful implementation. Consult with agricultural experts (e.g., KVK) for detailed local advice. All financial figures are estimates and subject to market fluctuations."
}}
**FARM RECORD FOR ANALYSIS:**
{json.dumps(record, indent=2, ensure_ascii=False)}
**PLANNING REQUIREMENTS:**
1. Generate a realistic and actionable plan specifically for {farming_type} for the year {next_year}.
2. All dates should be for {next_year} or {next_year+1} as appropriate for multi-year cycles.
3. Ensure all financial figures are in Indian Rupees (βΉ) and are plausible estimates for {record.get('area', 'N/A')} farm size.
4. Include specific names for inputs (pesticides, fertilizers, fodder varieties, feed types, breeds) and recommended practices relevant to India and {farming_type}.
5. Consider seasonal climate patterns, including monsoon.
6. Ensure the language of the entire plan (all text, headings, recommendations) is in {language}.
7. **Crucially, avoid generating information that is irrelevant to the {farming_type}. For example, if it's Dairy Farming, do not include paddy or wheat calendar details. Focus solely on fodder, animal health, milk production, etc.**
8. If a section is genuinely not applicable, provide a very brief statement like "Not applicable as primary farming is {farming_type}." but try to find a relevant equivalent.
Generate the complete yearly plan now:"""
return prompt
def create_daily_tasks_prompt(record, target_date_obj, latest_farmer_updates_context=""):
"""
Create a prompt for AI to generate daily tasks for a specific date,
incorporating latest farmer updates.
"""
farming_type = record.get('farming_type', 'Mixed Farming')
farmer_name = record.get('farmer_name', 'Farmer')
farm_name = record.get('farm_name', 'Unnamed Farm')
target_date_str = target_date_obj.strftime('%Y-%m-%d')
# Normalize preferred language for daily tasks prompt as well
raw_pref_lang = record.get('preferred_lang_time', 'English')
try:
if isinstance(raw_pref_lang, list) and raw_pref_lang:
preferred_lang = str(raw_pref_lang[0]).split('/')[0].strip() or 'English'
else:
preferred_lang = str(raw_pref_lang).split('/')[0].strip() or 'English'
except Exception:
preferred_lang = 'English'
# Extract relevant details from the record based on farming_type
context_details = []
context_details.append(f"'farming_type': '{farming_type}'")
context_details.append(f"'area': '{record.get('area', 'N/A')}'")
context_details.append(f"'soil_type': '{record.get('soil_type', 'N/A')}'")
context_details.append(f"'irrigation_source': '{record.get('irrigation_source', 'N/A')}'")
context_details.append(f"'water_quality': '{record.get('water_quality', 'N/A')}'")
if farming_type == 'Crop Farming':
if record.get('crops'):
for i, crop in enumerate(record['crops']):
context_details.append(f"'crop_{i+1}_primary_crop': '{crop.get('primary_crop', 'N/A')}'")
context_details.append(f"'crop_{i+1}_variety_cultivar': '{crop.get('variety_cultivar', 'N/A')}'")
context_details.append(f"'crop_{i+1}_planting_season_window': '{crop.get('planting_season_window', 'N/A')}'")
context_details.append(f"'crop_{i+1}_harvest_window': '{crop.get('harvest_window', 'N/A')}'")
context_details.append(f"'crop_{i+1}_constraints': '{crop.get('constraints', 'None')}'")
elif farming_type == 'Dairy Farming':
context_details.append(f"'dairy_num_animals_cows': '{record.get('dairy_num_animals_cows', '0')}'")
context_details.append(f"'dairy_num_animals_buffaloes': '{record.get('dairy_num_animals_buffaloes', '0')}'")
context_details.append(f"'dairy_avg_milk_production': '{record.get('dairy_avg_milk_production', 'N/A')} L/day'")
context_details.append(f"'dairy_fodder_source': '{', '.join(record.get('dairy_fodder_source', []))}'")
context_details.append(f"'dairy_veterinary_access': '{record.get('dairy_veterinary_access', 'N/A')}'")
elif farming_type == 'Poultry Farming':
context_details.append(f"'poultry_type': '{', '.join(record.get('poultry_type', []))}'")
context_details.append(f"'poultry_num_birds': '{record.get('poultry_num_birds', '0')}'")
context_details.append(f"'poultry_housing_type': '{record.get('poultry_housing_type', 'N/A')}'")
context_details.append(f"'poultry_feed_source': '{record.get('poultry_feed_source', 'N/A')}'")
context_details.append(f"'poultry_health_mgmt': '{record.get('poultry_health_mgmt', 'N/A')}'")
elif farming_type == 'Horticulture Farming':
context_details.append(f"'horti_type': '{', '.join(record.get('horti_type', []))}'")
if record.get('horti_crops'):
for i, crop in enumerate(record['horti_crops']):
context_details.append(f"'horti_crop_{i+1}_primary_crop': '{crop.get('primary_crop', 'N/A')}'")
context_details.append(f"'horti_crop_{i+1}_variety_cultivar': '{crop.get('variety_cultivar', 'N/A')}'")
context_details.append(f"'horti_crop_{i+1}_planting_season_window': '{crop.get('planting_season_window', 'N/A')}'")
elif farming_type == 'Plantation Farming':
context_details.append(f"'plantation_type': '{', '.join(record.get('plantation_type', []))}'")
context_details.append(f"'plantation_age_plants': '{record.get('plantation_age_plants', 'N/A')}'")
context_details.append(f"'plantation_yield_per_unit': '{record.get('plantation_yield_per_unit', 'N/A')}'")
context_str = ", ".join(context_details)
prompt = f"""
You are an AI farm assistant for {farmer_name}'s {farm_name}.
Based on the following farm details and the target date {target_date_str}, generate a list of 3-5 specific, actionable daily tasks.
Focus on immediate, practical actions relevant to the farm type, current season, and maintenance needs for {target_date_str}.
**CRITICAL INSTRUCTIONS:**
- Output ONLY valid JSON. Do NOT include markdown code blocks (```json).
- Provide tasks in {preferred_lang} language.
- Each task object MUST have the keys: "action" (string), "notes" (string), "source" (string), "priority" (string: "High", "Medium", "Low").
- "Source" can be "AI Recommendation", "Standard Operating Procedure", "Seasonal Requirement", "Farm Observation", "Preventive Maintenance", "Farmer Update", etc.
- If no specific critical tasks are identified, provide general monitoring and maintenance tasks.
- Consider the time of year (e.g., if it's a planting or harvest season for specific crops/animals).
- **IMPORTANT**: Analyze the 'Latest Farmer Updates' section below and integrate any relevant observations or issues into the task generation. For instance, if the farmer reported 'pest issue', include a task related to pest control. If 'water scarcity', include a water conservation task.
**FARM RECORD (Key Details):**
{{
"farmer_name": "{farmer_name}",
"farm_name": "{farm_name}",
{context_str}
}}
**TARGET DATE:** {target_date_str}
**LATEST FARMER UPDATES:**
{latest_farmer_updates_context}
{"(No recent updates relevant to daily tasks were found if this section is empty.)" if not latest_farmer_updates_context else ""}
**REQUIRED JSON STRUCTURE (only the JSON part):**
{{
"tasks": [
{{
"action": "Task description, specific and actionable.",
"notes": "Detailed notes or reasons for the task.",
"source": "Origin of the task (e.g., AI, SOP, Seasonal, Farmer Update).",
"priority": "High"
}},
// ... up to 5 tasks ...
]
}}
Generate the daily tasks now:
"""
return prompt
@app.route('/reset-data', methods=['POST'])
def reset_data():
try:
DATA_FILE.parent.mkdir(parents=True, exist_ok=True)
DATA_FILE.write_text('[]', encoding='utf-8')
# Optionally, clear the generated AI output directory as well
if GEN_AI_OUTPUT_DIR.exists():
for f in GEN_AI_OUTPUT_DIR.iterdir():
if f.is_file():
f.unlink()
# Clear telegram bot data as well
if load_telegram_data and save_telegram_data:
initial_telegram_data = {
"farmer_updates": [],
"scheduled_items": [],
"farmers": {},
"update_categories": list(UPDATE_QUESTIONS.keys())
}
save_telegram_data(initial_telegram_data)
print("Telegram bot data file reset.")
return jsonify({'status': 'ok', 'message': 'All data files cleared.'}), 200
except Exception as e:
return jsonify({'status': 'error', 'message': str(e)}), 500
@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
def csv_to_list(s):
return [x.strip() for x in s.split(',') if x.strip()]
def resolve_multi(name, other_name=None):
vals = request.form.getlist(name)
if not vals or (len(vals) == 1 and vals == ''): # Changed this line for empty multi-select case
s = request.form.get(name, '').strip()
vals = csv_to_list(s) if s else []
vals = [v.strip() for v in vals if v and v.strip()]
if other_name and ('other' in vals or 'custom' in vals):
other_text = request.form.get(other_name, '').strip()
if other_text:
vals = [v for v in vals if v not in ('other', 'custom')]
vals.append(other_text)
return vals
def resolve_single(name, other_name=None, default=''):
v = request.form.get(name, default)
if v is None:
return default
v = v.strip()
if other_name and v in ('other', 'custom'):
other_text = request.form.get(other_name, '').strip()
return other_text or v
return v
def parse_dynamic_sections(prefix):
items_data = []
indices = set()
for key in request.form:
if key.startswith(f'{prefix}[') and '].' in key:
try:
# Extract the index number correctly from 'prefix[index].field'
start_idx = key.find(f'{prefix}[') + len(f'{prefix}[')
end_idx = key.find('].', start_idx)
if start_idx != -1 and end_idx != -1:
index_str = key[start_idx:end_idx]
indices.add(int(index_str))
except ValueError:
pass
for i in sorted(list(indices)):
item_rec = {
'primary_crop': resolve_single(f'{prefix}[{i}].primary_crop'),
'variety_cultivar': resolve_single(f'{prefix}[{i}].variety_cultivar'),
'cropping_system': resolve_multi(f'{prefix}[{i}].cropping_system[]'),
'planting_season_window': resolve_single(f'{prefix}[{i}].planting_season_window'),
'harvest_window': resolve_single(f'{prefix}[{i}].harvest_window'),
'seed_source': resolve_single(f'{prefix}[{i}].seed_source'),
'past_history': resolve_single(f'{prefix}[{i}].past_history'),
'constraints': resolve_single(f'{prefix}[{i}].constraints'),
}
items_data.append(item_rec)
return items_data
rec = {
'id': str(uuid.uuid4()),
'timestamp': datetime.utcnow().isoformat() + 'Z',
'farmer_name': resolve_single('farmer_name'),
'phone': resolve_single('phone'),
'preferred_lang_time': resolve_single('preferred_lang_time'),
'farm_name': resolve_single('farm_name'),
'farming_type': resolve_single('farming_type'),
'geo': {
'latitude': float(resolve_single('latitude', default='0') or 0),
'longitude': float(resolve_single('longitude', default='0') or 0),
},
'area': resolve_single('area'),
'soil_type': resolve_single('soil_type'),
'soil_depth': resolve_single('soil_depth'),
'ph_category': resolve_single('ph_category'),
'organic_matter_level': resolve_single('organic_matter_level'),
'irrigation_source': resolve_single('irrigation_source'),
'water_quality': resolve_single('water_quality'),
'irrigation_infrastructure': resolve_multi('irrigation_infrastructure[]'),
'crops': [],
'horti_crops': [],
'dairy_num_animals_cows': resolve_single('dairy_num_animals_cows'),
'dairy_num_animals_buffaloes': resolve_single('dairy_num_animals_buffaloes'),
'dairy_num_animals_goats': resolve_single('dairy_num_animals_goats'),
'dairy_breeds': resolve_single('dairy_breeds'),
'dairy_avg_milk_production': float(resolve_single('dairy_avg_milk_production', default='0') or 0),
'dairy_fodder_source': resolve_multi('dairy_fodder_source[]'),
'dairy_veterinary_access': resolve_single('dairy_veterinary_access'),
'dairy_milk_storage': resolve_single('dairy_milk_storage'),
'dairy_market_access': resolve_single('dairy_market_access'),
'dairy_product_diversification': resolve_multi('dairy_product_diversification[]'),
'poultry_type': resolve_multi('poultry_type[]'),
'poultry_num_birds': resolve_single('poultry_num_birds'),
'poultry_housing_type': resolve_single('poultry_housing_type'),
'poultry_feed_source': resolve_single('poultry_feed_source'),
'poultry_health_mgmt': resolve_single('poultry_health_mgmt'),
'poultry_production_capacity': resolve_single('poultry_production_capacity'),
'poultry_market_access': resolve_single('poultry_market_access'),
'horti_type': resolve_multi('horti_type[]'),
'horti_protection_type': resolve_multi('horti_protection_type[]'),
'horti_post_harvest_handling': resolve_multi('horti_post_harvest_handling[]'),
'horti_market_access': resolve_single('horti_market_access'),
'plantation_type': resolve_multi('plantation_type[]'),
'plantation_area_each': resolve_single('plantation_area_each'),
'plantation_age_plants': resolve_single('plantation_age_plants'),
'plantation_processing_facilities': resolve_single('plantation_processing_facilities'),
'plantation_yield_per_unit': resolve_single('plantation_yield_per_unit'),
'plantation_market_access': resolve_single('plantation_market_access'),
'plantation_shade_management': resolve_single('plantation_shade_management'),
}
rec['crops'] = parse_dynamic_sections('crops')
rec['horti_crops'] = parse_dynamic_sections('horti_crops')
poly_raw = request.form.get('polygon_coords', '').strip()
if poly_raw:
try:
rec['polygon_coords'] = json.loads(poly_raw)
except Exception:
rec['polygon_coords'] = poly_raw
save_record(rec)
return render_template('success.html', record=rec)
return render_template('form.html')
@app.route('/generate-insights/<string:record_id>', methods=['GET'])
def generate_insights(record_id):
record = get_record_by_id(record_id)
if not record:
return "Record not found!", 404
insights = {}
if GOOGLE_API_KEY:
try:
model = genai.GenerativeModel(GEMINI_MODEL)
prompt = create_insights_prompt(record)
response = model.generate_content(prompt)
response_text = clean_ai_response(response.text)
insights = json.loads(response_text)
# --- Save the generated insights as JSON file ---
GEN_AI_OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
timestamp_str = datetime.now().strftime("%Y%m%d_%H%M%S")
insights_filename = GEN_AI_OUTPUT_DIR / f"{record_id}_insights_{timestamp_str}.json"
try:
with open(insights_filename, 'w', encoding='utf-8') as f:
json.dump(insights, f, indent=2, ensure_ascii=False)
insights['save_status'] = f"Insights successfully saved to {insights_filename.name}"
print(f"Insights for record {record_id} saved to {insights_filename}")
except Exception as file_e:
insights['save_status'] = f"Error saving insights to file: {file_e}"
print(f"Error saving insights for record {record_id}: {file_e}")
# --- End Save functionality ---
except json.JSONDecodeError as e:
error_message = f"Could not parse AI response into JSON. Error: {str(e)}. Raw response (truncated): {response_text[:1000]}..."
print(f"JSON Decode Error (Insights): {error_message}")
insights = {
"summary": "AI response parsing failed. The model did not return valid JSON.",
"key_metrics": {"error": "JSON parsing failed"},
"recommendations": ["Review the prompt for strict JSON adherence", "Check AI model response format"],
"risk_factors": ["Parsing error occurred, preventing structured insights retrieval"],
"growth_opportunities": [],
"data_visualizations": [],
"expert_commentary": error_message,
"save_status": "Insights not saved due to parsing error."
}
except Exception as e:
error_message = f"Error generating insights: {type(e).__name__} - {e}"
print(f"Gemini API Error (Insights): {error_message}")
insights = {
"summary": "AI insights generation failed due to an API error.",
"key_metrics": {"error": "API error"},
"recommendations": ["Ensure GOOGLE_API_KEY is correctly set", "Check network connection", "Review API usage limits"],
"risk_factors": ["AI generation failed due to API issues"],
"growth_opportunities": [],
"data_visualizations": [],
"expert_commentary": error_message,
"save_status": "Insights not saved due to API error."
}
else:
insights = {
"summary": "Gemini API key not configured. Cannot generate AI insights.",
"key_metrics": {"error": "No API key"},
"recommendations": ["Set GOOGLE_API_KEY environment variable"],
"risk_factors": ["API key missing"],
"growth_opportunities": [],
"data_visualizations": [],
"expert_commentary": "API key not found in environment variables.",
"save_status": "Insights not saved (API key missing)."
}
return render_template('insights.html', record=record, insights=insights)
@app.route('/generate-yearly-plan/<string:record_id>', methods=['GET'])
def generate_yearly_plan(record_id):
record = get_record_by_id(record_id)
if not record:
return "Record not found!", 404
plan = {}
if GOOGLE_API_KEY:
try:
model = genai.GenerativeModel(GEMINI_MODEL)
prompt = create_yearly_plan_prompt(record)
response = model.generate_content(prompt)
response_text = clean_ai_response(response.text)
plan = json.loads(response_text)
# --- Save the generated yearly plan as JSON file ---
GEN_AI_OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
timestamp_str = datetime.now().strftime("%Y%m%d_%H%M%S")
plan_filename = GEN_AI_OUTPUT_DIR / f"{record_id}_yearly_plan_{timestamp_str}.json"
try:
with open(plan_filename, 'w', encoding='utf-8') as f:
json.dump(plan, f, indent=2, ensure_ascii=False)
plan['save_status'] = f"Yearly plan successfully saved to {plan_filename.name}"
print(f"Yearly plan for record {record_id} saved to {plan_filename}")
except Exception as file_e:
plan['save_status'] = f"Error saving yearly plan to file: {file_e}"
print(f"Error saving yearly plan for record {record_id}: {file_e}")
# --- End Save functionality ---
except json.JSONDecodeError as e:
error_message = f"Could not parse AI plan response into JSON. Error: {str(e)}. Raw response (truncated): {response_text[:1000]}..."
print(f"JSON Decode Error (Yearly Plan): {error_message}")
plan = {
"executive_summary": {
"summary_paragraph": "Failed to generate plan summary due to JSON parsing error.",
"main_recommendation": "Check backend logs for details."
},
"annual_crop_calendar": [],
"irrigation_schedule": [],
"nutrient_fertilizer_schedule": [],
"pest_disease_monitoring_plan": [],
"labour_operations_calendar": [],
"input_procurement_budget_timeline": [],
"market_sales_guidance": [],
"sensors_monitoring_plan": [],
"risk_contingency_plan": [],
"tasks_reminders": [],
"metrics_evaluation": [],
"notes": error_message,
"save_status": "Yearly plan not saved due to parsing error."
}
except Exception as e:
error_message = f"Error generating yearly plan: {type(e).__name__} - {e}"
print(f"Gemini API Error (Yearly Plan): {error_message}")
plan = {
"executive_summary": {
"summary_paragraph": f"Failed to generate plan due to API error: {e}",
"main_recommendation": "Check API key and network."
},
"annual_crop_calendar": [],
"irrigation_schedule": [],
"nutrient_fertilizer_schedule": [],
"pest_disease_monitoring_plan": [],
"labour_operations_calendar": [],
"input_procurement_budget_timeline": [],
"market_sales_guidance": [],
"sensors_monitoring_plan": [],
"risk_contingency_plan": [],
"tasks_reminders": [],
"metrics_evaluation": [],
"notes": error_message,
"save_status": "Yearly plan not saved due to API error."
}
else:
plan = {
"executive_summary": {
"summary_paragraph": "Gemini API key not configured. Cannot generate yearly plan.",
"main_recommendation": "Please set the GOOGLE_API_KEY environment variable with your valid Gemini API key."
},
"annual_crop_calendar": [],
"irrigation_schedule": [],
"nutrient_fertilizer_schedule": [],
"pest_disease_monitoring_plan": [],
"labour_operations_calendar": [],
"input_procurement_budget_timeline": [],
"market_sales_guidance": [],
"sensors_monitoring_plan": [],
"risk_contingency_plan": [],
"tasks_reminders": [],
"metrics_evaluation": [],
"notes": "API key not found in environment variables.",
"save_status": "Yearly plan not saved (API key missing)."
}
return render_template('yearly_plan.html', record=record, plan=plan)
# --- New Routes for Telegram Bot Integration ---
@app.route('/daily-tasks/<string:record_id>', methods=['GET'])
def daily_tasks(record_id):
record = get_record_by_id(record_id)
if not record:
return "Record not found!", 404
# Get date from query parameter, default to today
date_str = request.args.get('date', datetime.now().strftime('%Y-%m-%d'))
try:
display_date = datetime.strptime(date_str, '%Y-%m-%d').date()
except ValueError:
display_date = date.today()
date_str = display_date.strftime('%Y-%m-%d') # Re-assign valid date_str
tasks = []
latest_updates_context = "No recent updates from farmer." # Default context
if get_latest_farmer_updates and load_telegram_data:
telegram_data = load_telegram_data()
farmer_telegram_id = None
# Attempt to find the farmer's Telegram ID based on name match
for tid, farmer_info in telegram_data.get("farmers", {}).items():
if farmer_info.get("name", "").lower() == record.get("farmer_name", "").lower():
farmer_telegram_id = tid
break
if farmer_telegram_id:
recent_updates = get_latest_farmer_updates(farmer_telegram_id, num_updates=3)
if recent_updates:
latest_updates_context = "Latest farmer updates relevant to task generation:\n" + "\n".join([
f"- {u['category'].replace('_', ' ').title()}: {u['update_text']} (on {u['date']})" for u in recent_updates
])
else:
latest_updates_context = f"No recent updates found from '{record.get('farmer_name')}' (Telegram ID: {farmer_telegram_id})."
else:
latest_updates_context = f"Telegram user for '{record.get('farmer_name')}' not found in bot's records. Farmer needs to send a message to the bot first."
if GOOGLE_API_KEY:
try:
model = genai.GenerativeModel(GEMINI_MODEL)
prompt = create_daily_tasks_prompt(record, display_date, latest_updates_context)
response = model.generate_content(prompt)
response_text = clean_ai_response(response.text)
# The AI is instructed to return a JSON object with a "tasks" key
parsed_response = json.loads(response_text)
tasks = parsed_response.get('tasks', [])
# --- Save the generated daily tasks as JSON file ---
GEN_AI_OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
timestamp_str = datetime.now().strftime("%Y%m%d_%H%M%S")
tasks_filename = GEN_AI_OUTPUT_DIR / f"{record_id}_daily_tasks_{date_str}_{timestamp_str}.json"
try:
with open(tasks_filename, 'w', encoding='utf-8') as f:
json.dump(parsed_response, f, indent=2, ensure_ascii=False) # Save the whole parsed_response
print(f"Daily tasks for record {record_id} on {date_str} saved to {tasks_filename}")
except Exception as file_e:
print(f"Error saving daily tasks for record {record_id} on {date_str}: {file_e}")
# --- End Save functionality ---
except json.JSONDecodeError as e:
error_message = f"Could not parse AI daily tasks response into JSON. Error: {str(e)}. Raw response (truncated): {response_text[:1000]}..."
print(f"JSON Decode Error (Daily Tasks): {error_message}")
tasks = [{"action": "AI response parsing failed", "notes": error_message, "source": "System Error", "priority": "High"}]
except Exception as e:
error_message = f"Error generating daily tasks: {type(e).__name__} - {e}"
print(f"Gemini API Error (Daily Tasks): {error_message}")
tasks = [{"action": "AI tasks generation failed", "notes": error_message, "source": "System Error", "priority": "High"}]
else:
tasks = [{"action": "Gemini API key not configured", "notes": "Please set the GOOGLE_API_KEY environment variable.", "source": "Configuration Error", "priority": "High"}]
return render_template('daily_tasks.html', record=record, tasks=tasks, display_date=display_date, latest_updates_context=latest_updates_context)
@app.route('/send_question', methods=['POST'])
def send_immediate_question():
ok, missing, err = ensure_telegram_loaded()
if not ok:
msg = f"Telegram features not available. Missing: {', '.join(missing)}"
if err:
msg += f"; import error: {err}"
print(msg)
return jsonify({'status': 'error', 'message': msg, 'missing': missing}), 500
question_text = request.form.get('question', '').strip()
category = request.form.get('category', 'general_update')
record_id = request.form.get('record_id') # Hidden field to pass record_id
# Get farmer's Telegram ID
record = get_record_by_id(record_id)
if not record:
return jsonify({'status': 'error', 'message': 'Farmer record not found!'}), 404
telegram_data = load_telegram_data()
farmer_telegram_id = None
for tid, farmer_info in telegram_data.get("farmers", {}).items():
if farmer_info.get("name", "").lower() == record.get("farmer_name", "").lower():
farmer_telegram_id = tid
break
if not farmer_telegram_id:
return jsonify({'status': 'error', 'message': f'Telegram user ID for farmer "{record.get("farmer_name")}" not found. Farmer needs to send a message to the bot first to register.'}), 400
final_question = question_text
if not final_question: # If custom question is empty, use predefined
final_question = UPDATE_QUESTIONS.get(category, "Please provide an update on your farm.")
if send_message(farmer_telegram_id, f"π *Immediate Update Request from Farm Portal for {record.get('farmer_name')}*:\n\n{final_question}"):
# Also save to farmer_updates.json for tracking
data = load_telegram_data()
data["farmer_updates"].append({
"farmer_name": "Farm Portal System",
"farmer_id": "system_admin", # Using a generic ID for system messages
"update_text": f"[IMMEDIATE QUESTION SENT to {record.get('farmer_name')}] {final_question}",
"category": "system_message",
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"date": datetime.now().strftime("%Y-%m-%d")
})
save_telegram_data(data)
return jsonify({'status': 'ok', 'message': 'Update request sent!'}), 200
else:
return jsonify({'status': 'error', 'message': 'Failed to send update request.'}), 500
@app.route('/schedule_daily_question', methods=['POST'])
def schedule_daily_question():
ok, missing, err = ensure_telegram_loaded()
if not ok:
msg = f"Telegram features not available. Missing: {', '.join(missing)}"
if err:
msg += f"; import error: {err}"
print(msg)
return jsonify({'status': 'error', 'message': msg, 'missing': missing}), 500
daily_question = request.form.get('daily_question', '').strip()
daily_time = request.form.get('daily_time')
frequency = request.form.get('frequency')
use_predefined = 'use_predefined' in request.form
question_category = request.form.get('question_category')
custom_days = request.form.getlist('custom_days')
record_id = request.form.get('record_id') # Hidden field to pass record_id
if not daily_time:
return jsonify({'status': 'error', 'message': 'Time is required for scheduling.'}), 400
record = get_record_by_id(record_id)
if not record:
return jsonify({'status': 'error', 'message': 'Farmer record not found!'}), 404
telegram_data = load_telegram_data()
farmer_telegram_id = None
for tid, farmer_info in telegram_data.get("farmers", {}).items():
if farmer_info.get("name", "").lower() == record.get("farmer_name", "").lower():
farmer_telegram_id = tid
break
if not farmer_telegram_id:
return jsonify({'status': 'error', 'message': f'Telegram user ID for farmer "{record.get("farmer_name")}" not found. Farmer needs to send a message to the bot first to register.'}), 400
# If use_predefined is true and daily_question is empty, fill it from UPDATE_QUESTIONS
if use_predefined and not daily_question:
daily_question = UPDATE_QUESTIONS.get(question_category, "Please provide an update on your farm.")
schedule_id = add_scheduled_item(
item_type="question",
chat_id=farmer_telegram_id, # Target specific farmer's chat
text=daily_question,
time=daily_time,
frequency=frequency,
day_of_week=None,
custom_days=custom_days if frequency == 'custom' else None,
record_id=record_id, # Link for context, though message is generic
category=question_category,
use_predefined=use_predefined
)
if schedule_id:
return jsonify({'status': 'ok', 'message': 'Question scheduled successfully!', 'schedule_id': schedule_id}), 200
else:
return jsonify({'status': 'error', 'message': 'Failed to schedule question.'}), 500
@app.route('/api/generate_and_fetch_daily_tasks/<string:record_id>/<string:target_date_str>', methods=['GET'])
def api_generate_and_fetch_daily_tasks(record_id, target_date_str):
"""
API endpoint for the Telegram bot's scheduler to fetch dynamically generated daily tasks.
"""
record = get_record_by_id(record_id)
if not record:
return jsonify({'error': 'Farmer record not found'}), 404
if not GOOGLE_API_KEY:
return jsonify({'error': 'Gemini API key not configured.'}), 500
ok, missing, err = ensure_telegram_loaded()
if not ok:
msg = f"Telegram features not available. Missing: {', '.join(missing)}"
if err:
msg += f"; import error: {err}"
print(msg)
return jsonify({'status': 'error', 'message': msg, 'missing': missing}), 500
try:
display_date = datetime.strptime(target_date_str, '%Y-%m-%d').date()
except ValueError:
return jsonify({'error': 'Invalid date format'}), 400
tasks_list = []
latest_updates_context = "No recent updates from farmer."
telegram_data = load_telegram_data()
farmer_telegram_id = None
for tid, farmer_info in telegram_data.get("farmers", {}).items():
if farmer_info.get("name", "").lower() == record.get("farmer_name", "").lower():
farmer_telegram_id = tid
break
if farmer_telegram_id:
recent_updates = get_latest_farmer_updates(farmer_telegram_id, num_updates=3)
if recent_updates:
latest_updates_context = "Latest farmer updates relevant to task generation:\n" + "\n".join([
f"- {u['category'].replace('_', ' ').title()}: {u['update_text']} (on {u['date']})" for u in recent_updates
])
else:
latest_updates_context = f"No recent updates found from '{record.get('farmer_name')}' (Telegram ID: {farmer_telegram_id})."
else:
latest_updates_context = f"Telegram user for '{record.get('farmer_name')}' not found in bot's records. Farmer needs to send a message to the bot first."
try:
model = genai.GenerativeModel(GEMINI_MODEL)
prompt = create_daily_tasks_prompt(record, display_date, latest_updates_context)
response = model.generate_content(prompt)
response_text = clean_ai_response(response.text)
parsed_response = json.loads(response_text)
tasks_list = parsed_response.get('tasks', [])
# Format tasks for Telegram using Markdown
formatted_tasks = []
if tasks_list:
for t in tasks_list:
action = t.get('action', 'N/A')
priority = t.get('priority', 'Low')
notes = t.get('notes', '')
source = t.get('source', 'AI Recommendation')
task_string = f"β‘οΈ *{action}* (Priority: {priority})"
if notes:
task_string += f"\n _Notes: {notes}_"
task_string += f"\n _Source: {source}_"
formatted_tasks.append(task_string)
message_content = f"ποΈ *Daily Tasks for {record.get('farmer_name', 'Your Farm')} ({target_date_str})* ποΈ\n\n"
if formatted_tasks:
message_content += "\n\n".join(formatted_tasks)
else:
message_content += "No specific tasks identified for today. Keep monitoring your farm!"
return jsonify({'status': 'ok', 'message': message_content}), 200
except Exception as e:
error_message = f"Failed to generate tasks for {record_id} on {target_date_str}: {type(e).__name__} - {e}"
print(error_message)
return jsonify({'status': 'error', 'error': error_message}), 500
@app.route('/schedule_daily_task_delivery/<string:record_id>', methods=['POST'])
def schedule_daily_task_delivery(record_id):
ok, missing, err = ensure_telegram_loaded()
if not ok:
msg = f"Telegram features not available. Missing: {', '.join(missing)}"
if err:
msg += f"; import error: {err}"
print(msg)
return jsonify({'status': 'error', 'message': msg, 'missing': missing}), 500
record = get_record_by_id(record_id)
if not record:
return jsonify({'status': 'error', 'message': 'Farmer record not found!'}), 404
daily_time = request.form.get('daily_time_task')
frequency = request.form.get('frequency_task', 'daily')
custom_days = request.form.getlist('custom_days_task')
if not daily_time:
return jsonify({'status': 'error', 'message': 'Time is required for scheduling daily tasks.'}), 400
# Get farmer's Telegram ID to send the task to
telegram_data = load_telegram_data()
farmer_telegram_id = None
for tid, farmer_info in telegram_data.get("farmers", {}).items():
if farmer_info.get("name", "").lower() == record.get("farmer_name", "").lower():
farmer_telegram_id = tid
break
if not farmer_telegram_id:
return jsonify({'status': 'error', 'message': f'Telegram user ID for farmer "{record.get("farmer_name")}" not found. Farmer needs to send a message to the bot first to register.'}), 400
schedule_id = add_scheduled_item(
item_type="daily_task",
chat_id=farmer_telegram_id, # Target specific farmer's chat
text="{FETCH_DAILY_TASK_FROM_API}", # Placeholder: scheduler will fetch content from API
time=daily_time,
frequency=frequency,
custom_days=custom_days if frequency == 'custom' else None,
record_id=record_id, # IMPORTANT: Link back to the app's farmer record for task generation
category="daily_task_delivery",
use_predefined=False
)
if schedule_id:
return jsonify({'status': 'ok', 'message': 'Daily tasks scheduled for delivery!'}), 200
else:
return jsonify({'status': 'error', 'message': 'Failed to schedule daily tasks.'}), 500
if __name__ == '__main__':
DATA_FILE.parent.mkdir(parents=True, exist_ok=True)
GEN_AI_OUTPUT_DIR.mkdir(parents=True, exist_ok=True) # Ensure the output directory exists
# Set FLASK_APP_URL environment variable for the telegram bot to call back
# For local testing, this will be http://localhost:5000
# For deployment, this needs to be the publicly accessible URL of your Flask app
os.environ.setdefault("FLASK_APP_URL", "http://localhost:5000")
# Start telegram bot threads if available
if start_telegram_bot:
try:
start_telegram_bot()
print(f'Telegram bot polling/scheduler started. FLASK_APP_URL for callbacks: {os.environ.get("FLASK_APP_URL")}')
except Exception as e:
print(f'Failed to start telegram bot threads: {e}')
try:
app.run(debug=True, host='0.0.0.0', port=5000)
finally:
# Attempt to stop telegram bot threads on shutdown
if stop_telegram_bot:
try:
stop_telegram_bot()
print('Telegram bot stopped')
except Exception as e:
print(f'Error stopping telegram bot: {e}')
|