pranit144's picture
Upload 16 files
12e0bb3 verified
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}')