Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import pandas as pd | |
| import numpy as np | |
| from datetime import datetime, timedelta | |
| import logging | |
| import requests | |
| from typing import Optional, Dict, List, Any | |
| import json | |
| from io import BytesIO | |
| from PIL import Image | |
| import time | |
| import os | |
| import sqlite3 | |
| import hashlib | |
| # OpenAI Integration | |
| try: | |
| from openai import OpenAI | |
| OPENAI_AVAILABLE = True | |
| except ImportError: | |
| OPENAI_AVAILABLE = False | |
| # Image Recognition | |
| try: | |
| import tensorflow as tf | |
| import cv2 | |
| IMAGE_RECOGNITION_AVAILABLE = True | |
| except ImportError: | |
| IMAGE_RECOGNITION_AVAILABLE = False | |
| # Voice I/O | |
| try: | |
| import speech_recognition as sr | |
| from gtts import gTTS | |
| VOICE_AVAILABLE = True | |
| except ImportError: | |
| VOICE_AVAILABLE = False | |
| # ML Models | |
| try: | |
| from sklearn.ensemble import RandomForestRegressor | |
| from sklearn.preprocessing import StandardScaler | |
| import joblib | |
| ML_AVAILABLE = True | |
| except ImportError: | |
| ML_AVAILABLE = False | |
| # Data Export | |
| try: | |
| from reportlab.lib.pagesizes import letter | |
| from reportlab.pdfgen import canvas | |
| EXPORT_AVAILABLE = True | |
| except ImportError: | |
| EXPORT_AVAILABLE = False | |
| # Data Visualization | |
| try: | |
| import plotly.graph_objects as go | |
| import plotly.express as px | |
| PLOTLY_AVAILABLE = True | |
| except ImportError: | |
| PLOTLY_AVAILABLE = False | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # STREAMLIT PAGE CONFIG | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| st.set_page_config( | |
| page_title="๐พ Farmer Copilot v3.0 - Complete", | |
| page_icon="๐", | |
| layout="wide", | |
| initial_sidebar_state="expanded" | |
| ) | |
| def inject_css(): | |
| st.markdown(""" | |
| <style> | |
| .main { | |
| background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); | |
| padding: 20px; | |
| } | |
| [data-testid="stSidebar"] { | |
| background: linear-gradient(180deg, #1a472a 0%, #2d5a3d 100%); | |
| } | |
| h1, h2, h3 { | |
| color: #1a472a; | |
| font-weight: 700; | |
| text-shadow: 0 2px 4px rgba(0,0,0,0.1); | |
| } | |
| .stButton > button { | |
| background: linear-gradient(135deg, #2d915e 0%, #1a472a 100%); | |
| color: white; | |
| font-weight: bold; | |
| border: none; | |
| border-radius: 8px; | |
| padding: 10px 24px; | |
| transition: all 0.3s ease; | |
| } | |
| .stButton > button:hover { | |
| box-shadow: 0 4px 12px rgba(45, 145, 94, 0.4); | |
| transform: translateY(-2px); | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| inject_css() | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # FEATURE 1: IMAGE RECOGNITION | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def load_disease_model(): | |
| """Load pre-trained disease detection model""" | |
| if not IMAGE_RECOGNITION_AVAILABLE: | |
| return None | |
| try: | |
| model = tf.keras.applications.MobileNetV2( | |
| weights='imagenet', | |
| input_shape=(224, 224, 3) | |
| ) | |
| return model | |
| except: | |
| return None | |
| def analyze_plant_disease(image_file): | |
| """Analyze plant leaf for diseases""" | |
| if not IMAGE_RECOGNITION_AVAILABLE: | |
| return None, "Image recognition not available" | |
| try: | |
| image = Image.open(image_file).convert('RGB') | |
| image_array = np.array(image.resize((224, 224))) / 255.0 | |
| image_array = np.expand_dims(image_array, axis=0) | |
| model = load_disease_model() | |
| if model is None: | |
| return None, "Model not loaded" | |
| predictions = model.predict(image_array) | |
| disease_map = { | |
| 0: "Early Blight - Use fungicide", | |
| 1: "Late Blight - Spray mancozeb", | |
| 2: "Powdery Mildew - Apply sulfur", | |
| 3: "Leaf Spot - Spray neem oil", | |
| 4: "Healthy Plant - No disease" | |
| } | |
| predicted_disease_idx = np.argmax(predictions[0]) | |
| confidence = float(predictions[0][predicted_disease_idx]) * 100 | |
| disease_name = disease_map.get(predicted_disease_idx, "Unknown") | |
| return { | |
| "disease": disease_name, | |
| "confidence": confidence, | |
| "severity": "High" if confidence > 80 else "Medium" if confidence > 50 else "Low" | |
| }, None | |
| except Exception as e: | |
| return None, f"Error: {str(e)}" | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # FEATURE 2: VOICE I/O | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def voice_input(): | |
| """Capture voice input from microphone""" | |
| if not VOICE_AVAILABLE: | |
| return None, "Voice feature not available" | |
| try: | |
| recognizer = sr.Recognizer() | |
| with sr.Microphone() as source: | |
| st.info("๐๏ธ Listening...") | |
| recognizer.adjust_for_ambient_noise(source, duration=1) | |
| audio = recognizer.listen(source, timeout=10) | |
| text = recognizer.recognize_google(audio, language='en-IN') | |
| return text, None | |
| except Exception as e: | |
| return None, f"Error: {str(e)}" | |
| def voice_output(text, language="en"): | |
| """Convert text to speech""" | |
| if not VOICE_AVAILABLE: | |
| return | |
| try: | |
| tts = gTTS(text=text, lang=language, slow=False) | |
| audio_file = "response.mp3" | |
| tts.save(audio_file) | |
| with open(audio_file, "rb") as f: | |
| audio_bytes = f.read() | |
| st.audio(audio_bytes, format="audio/mp3") | |
| if os.path.exists(audio_file): | |
| os.remove(audio_file) | |
| except Exception as e: | |
| st.error(f"Voice error: {str(e)}") | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # FEATURE 3: HISTORICAL DATA TRACKING | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def init_farm_database(): | |
| """Initialize SQLite database for farm data""" | |
| conn = sqlite3.connect('farm_data.db') | |
| c = conn.cursor() | |
| c.execute('''CREATE TABLE IF NOT EXISTS yields | |
| (id INTEGER PRIMARY KEY, | |
| date TEXT, | |
| crop TEXT, | |
| area REAL, | |
| yield REAL, | |
| location TEXT)''') | |
| conn.commit() | |
| conn.close() | |
| def save_farm_data(crop, area, yield_amount, location): | |
| """Save yield data to database""" | |
| conn = sqlite3.connect('farm_data.db') | |
| c = conn.cursor() | |
| date = datetime.now().strftime("%Y-%m-%d") | |
| c.execute('INSERT INTO yields VALUES (NULL, ?, ?, ?, ?, ?)', | |
| (date, crop, area, yield_amount, location)) | |
| conn.commit() | |
| conn.close() | |
| def get_historical_yields(crop=None, days=90): | |
| """Get historical yield data""" | |
| conn = sqlite3.connect('farm_data.db') | |
| if crop: | |
| df = pd.read_sql_query( | |
| f"SELECT * FROM yields WHERE crop='{crop}' ORDER BY date DESC LIMIT 10", | |
| conn | |
| ) | |
| else: | |
| df = pd.read_sql_query( | |
| f"SELECT * FROM yields ORDER BY date DESC LIMIT 10", | |
| conn | |
| ) | |
| conn.close() | |
| return df | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # FEATURE 4: DATA EXPORT | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def export_to_csv(data_dict, filename="farm_report"): | |
| """Export data to CSV""" | |
| df = pd.DataFrame(data_dict) | |
| csv_buffer = BytesIO() | |
| df.to_csv(csv_buffer, index=False) | |
| csv_buffer.seek(0) | |
| return csv_buffer, f"{filename}.csv" | |
| def export_to_excel(data_dict, filename="farm_report"): | |
| """Export data to Excel""" | |
| df = pd.DataFrame(data_dict) | |
| excel_buffer = BytesIO() | |
| try: | |
| with pd.ExcelWriter(excel_buffer, engine='openpyxl') as writer: | |
| df.to_excel(writer, sheet_name='Farm Data', index=False) | |
| except: | |
| df.to_excel(excel_buffer, sheet_name='Farm Data', index=False) | |
| excel_buffer.seek(0) | |
| return excel_buffer, f"{filename}.xlsx" | |
| def export_to_pdf(report_text, filename="farm_report"): | |
| """Export report to PDF""" | |
| if not EXPORT_AVAILABLE: | |
| return None, None | |
| pdf_buffer = BytesIO() | |
| c = canvas.Canvas(pdf_buffer, pagesize=letter) | |
| width, height = letter | |
| y_position = height - 50 | |
| c.setFont("Helvetica-Bold", 16) | |
| c.drawString(50, y_position, "Farmer Copilot Report") | |
| y_position -= 30 | |
| c.setFont("Helvetica", 10) | |
| for line in report_text.split('\n'): | |
| if y_position < 50: | |
| c.showPage() | |
| y_position = height - 50 | |
| c.drawString(50, y_position, line[:80]) | |
| y_position -= 15 | |
| c.save() | |
| pdf_buffer.seek(0) | |
| return pdf_buffer, f"{filename}.pdf" | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # FEATURE 5: SMART NOTIFICATIONS | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def check_weather_alerts(weather_data): | |
| """Check weather for farming alerts""" | |
| alerts = [] | |
| if weather_data: | |
| temp = weather_data.get('temperature', 0) | |
| humidity = weather_data.get('humidity', 0) | |
| if temp < 0: | |
| alerts.append({'message': 'โ๏ธ Frost Risk! Protect delicate crops', 'severity': 'HIGH'}) | |
| elif temp > 40: | |
| alerts.append({'message': '๐ฅ High Temperature! Increase irrigation', 'severity': 'HIGH'}) | |
| if humidity > 85: | |
| alerts.append({'message': '๐ฆ High Humidity! Watch for fungal diseases', 'severity': 'MEDIUM'}) | |
| return alerts | |
| def display_alerts(): | |
| """Display all alerts in sidebar""" | |
| with st.sidebar: | |
| st.markdown("### ๐ Smart Alerts") | |
| all_alerts = [] | |
| try: | |
| weather = get_weather_data(st.session_state.location) | |
| all_alerts.extend(check_weather_alerts(weather)) | |
| except: | |
| pass | |
| if all_alerts: | |
| for alert in all_alerts: | |
| if alert['severity'] == 'HIGH': | |
| st.error(alert['message']) | |
| elif alert['severity'] == 'MEDIUM': | |
| st.warning(alert['message']) | |
| else: | |
| st.info(alert['message']) | |
| else: | |
| st.success("โ All conditions normal!") | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # FEATURE 6: REAL-TIME MARKET PRICES | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def get_live_market_prices(): | |
| """Get live market prices""" | |
| return { | |
| "Wheat": 2250, "Rice": 2650, "Cotton": 5800, "Sugarcane": 295, | |
| "Potato": 1650, "Tomato": 1350, "Onion": 1950, "Corn": 2000 | |
| } | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # FEATURE 7: USER AUTHENTICATION | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def init_user_database(): | |
| """Initialize user database""" | |
| conn = sqlite3.connect('users.db') | |
| c = conn.cursor() | |
| c.execute('''CREATE TABLE IF NOT EXISTS users | |
| (id INTEGER PRIMARY KEY, | |
| username TEXT UNIQUE, | |
| password TEXT, | |
| email TEXT, | |
| location TEXT, | |
| created_date TEXT)''') | |
| conn.commit() | |
| conn.close() | |
| def hash_password(password): | |
| """Hash password""" | |
| return hashlib.sha256(password.encode()).hexdigest() | |
| def register_user(username, password, email, location): | |
| """Register new user""" | |
| try: | |
| conn = sqlite3.connect('users.db') | |
| c = conn.cursor() | |
| hashed_pwd = hash_password(password) | |
| date = datetime.now().strftime("%Y-%m-%d") | |
| c.execute('INSERT INTO users VALUES (NULL, ?, ?, ?, ?, ?)', | |
| (username, hashed_pwd, email, location, date)) | |
| conn.commit() | |
| conn.close() | |
| return True, "User registered successfully!" | |
| except sqlite3.IntegrityError: | |
| return False, "Username already exists" | |
| except Exception as e: | |
| return False, str(e) | |
| def login_user(username, password): | |
| """Login user""" | |
| try: | |
| conn = sqlite3.connect('users.db') | |
| c = conn.cursor() | |
| hashed_pwd = hash_password(password) | |
| c.execute('SELECT * FROM users WHERE username=? AND password=?', | |
| (username, hashed_pwd)) | |
| user = c.fetchone() | |
| conn.close() | |
| return (True, user) if user else (False, "Invalid credentials") | |
| except Exception as e: | |
| return False, str(e) | |
| def get_user_profile(username): | |
| """Get user profile""" | |
| conn = sqlite3.connect('users.db') | |
| c = conn.cursor() | |
| c.execute('SELECT * FROM users WHERE username=?', (username,)) | |
| user = c.fetchone() | |
| conn.close() | |
| return user | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # FEATURE 8: SOIL HEALTH MONITORING | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def init_soil_database(): | |
| """Initialize soil data database""" | |
| conn = sqlite3.connect('soil_data.db') | |
| c = conn.cursor() | |
| c.execute('''CREATE TABLE IF NOT EXISTS soil_tests | |
| (id INTEGER PRIMARY KEY, | |
| date TEXT, | |
| location TEXT, | |
| pH REAL, | |
| nitrogen INTEGER, | |
| phosphorus INTEGER, | |
| potassium INTEGER, | |
| organic_matter REAL, | |
| moisture REAL)''') | |
| conn.commit() | |
| conn.close() | |
| def save_soil_test(location, pH, nitrogen, phosphorus, potassium, organic_matter, moisture): | |
| """Save soil test results""" | |
| conn = sqlite3.connect('soil_data.db') | |
| c = conn.cursor() | |
| date = datetime.now().strftime("%Y-%m-%d") | |
| c.execute('''INSERT INTO soil_tests | |
| VALUES (NULL, ?, ?, ?, ?, ?, ?, ?, ?)''', | |
| (date, location, pH, nitrogen, phosphorus, potassium, organic_matter, moisture)) | |
| conn.commit() | |
| conn.close() | |
| def get_soil_recommendations(pH, nitrogen, phosphorus, potassium): | |
| """Get fertilizer recommendations""" | |
| recommendations = [] | |
| if pH < 6.0: | |
| recommendations.append("๐ด **Acidic Soil**: Apply lime (CaCO3)") | |
| elif pH > 8.0: | |
| recommendations.append("๐ด **Alkaline Soil**: Apply sulfur or organic matter") | |
| else: | |
| recommendations.append("โ **Ideal pH**: Between 6.5-7.5") | |
| if nitrogen < 200: | |
| recommendations.append("๐ก **Low Nitrogen**: Apply NPK 20:20:0") | |
| elif nitrogen > 500: | |
| recommendations.append("๐ก **High Nitrogen**: Reduce nitrogen fertilizer") | |
| else: | |
| recommendations.append("โ **Optimal Nitrogen**: Good") | |
| if phosphorus < 10: | |
| recommendations.append("๐ก **Low Phosphorus**: Apply DAP or SSP") | |
| elif phosphorus > 30: | |
| recommendations.append("๐ก **High Phosphorus**: No additional needed") | |
| else: | |
| recommendations.append("โ **Optimal Phosphorus**: Good") | |
| if potassium < 100: | |
| recommendations.append("๐ก **Low Potassium**: Apply KCl or MOP") | |
| elif potassium > 300: | |
| recommendations.append("๐ก **High Potassium**: Reduce fertilizer") | |
| else: | |
| recommendations.append("โ **Optimal Potassium**: Good") | |
| return recommendations | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # FEATURE 9: YIELD PREDICTION ML | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def train_yield_model(): | |
| """Train ML model for yield prediction""" | |
| if not ML_AVAILABLE: | |
| return None, None | |
| X_train = np.array([ | |
| [25, 80, 250, 20, 2.5], | |
| [28, 70, 200, 6.5, 3.0], | |
| [22, 85, 300, 6.8, 2.8], | |
| [26, 75, 250, 7.0, 3.2], | |
| ]) | |
| y_train = np.array([25.5, 23.0, 28.5, 26.0]) | |
| model = RandomForestRegressor(n_estimators=100, random_state=42) | |
| scaler = StandardScaler() | |
| X_scaled = scaler.fit_transform(X_train) | |
| model.fit(X_scaled, y_train) | |
| joblib.dump(model, 'yield_model.pkl') | |
| joblib.dump(scaler, 'scaler.pkl') | |
| return model, scaler | |
| def load_yield_model(): | |
| """Load trained yield prediction model""" | |
| if not ML_AVAILABLE: | |
| return None, None | |
| try: | |
| model = joblib.load('yield_model.pkl') | |
| scaler = joblib.load('scaler.pkl') | |
| return model, scaler | |
| except: | |
| return None, None | |
| def predict_yield(temperature, humidity, rainfall, pH, organic_matter): | |
| """Predict crop yield""" | |
| if not ML_AVAILABLE: | |
| return 22.0 | |
| model, scaler = load_yield_model() | |
| if model is None: | |
| model, scaler = train_yield_model() | |
| if model is None: | |
| return 22.0 | |
| features = np.array([[temperature, humidity, rainfall, pH, organic_matter]]) | |
| features_scaled = scaler.transform(features) | |
| yield_pred = model.predict(features_scaled)[0] | |
| return max(0, yield_pred) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # FEATURE 10: 7-DAY WEATHER FORECAST | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def get_7day_forecast(location): | |
| """Get 7-day weather forecast""" | |
| try: | |
| api_key = st.secrets.get("OPENWEATHER_API_KEY") | |
| if not api_key: | |
| return None | |
| geo_url = "https://api.openweathermap.org/geo/1.0/direct" | |
| geo_params = {"q": location, "limit": 1, "appid": api_key} | |
| geo_resp = requests.get(geo_url, params=geo_params) | |
| if not geo_resp.json(): | |
| return None | |
| lat, lon = geo_resp.json()[0]['lat'], geo_resp.json()[0]['lon'] | |
| forecast_url = "https://api.openweathermap.org/data/2.5/forecast" | |
| forecast_params = { | |
| "lat": lat, "lon": lon, "appid": api_key, | |
| "units": "metric", "cnt": 56 | |
| } | |
| forecast_resp = requests.get(forecast_url, params=forecast_params) | |
| forecast_data = forecast_resp.json() | |
| forecast_list = [] | |
| for item in forecast_data['list'][::8]: | |
| forecast_list.append({ | |
| 'Date': datetime.fromtimestamp(item['dt']).strftime("%a, %d %b"), | |
| 'Temp': f"{item['main']['temp']:.1f}ยฐC", | |
| 'Humidity': f"{item['main']['humidity']}%", | |
| 'Condition': item['weather'][0]['main'], | |
| 'Wind': f"{item['wind']['speed']:.1f} m/s" | |
| }) | |
| return pd.DataFrame(forecast_list) | |
| except Exception as e: | |
| return None | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # OPENAI SETUP | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def initialize_openai(): | |
| """Initialize OpenAI client""" | |
| if not OPENAI_AVAILABLE: | |
| return None, "OpenAI library not installed" | |
| api_key = None | |
| try: | |
| if hasattr(st, 'secrets') and "OPENAI_API_KEY" in st.secrets: | |
| api_key = st.secrets["OPENAI_API_KEY"] | |
| except: | |
| pass | |
| if not api_key: | |
| api_key = os.environ.get("OPENAI_API_KEY") | |
| if api_key and api_key.strip(): | |
| try: | |
| client = OpenAI(api_key=api_key.strip()) | |
| return client, None | |
| except Exception as e: | |
| return None, f"Failed: {str(e)}" | |
| else: | |
| return None, "No API key found" | |
| def get_ai_response(client, user_message: str, context: Dict, language: str = "English") -> str: | |
| """Get response from OpenAI GPT""" | |
| try: | |
| if not client: | |
| return "AI service not available." | |
| system_prompt = "You are an expert agricultural advisor. Provide helpful farming advice." | |
| location = context.get("location", "India") | |
| messages = [ | |
| {"role": "system", "content": system_prompt}, | |
| {"role": "user", "content": f"Location: {location}\n\n{user_message}"} | |
| ] | |
| response = client.chat.completions.create( | |
| model="gpt-3.5-turbo", | |
| messages=messages, | |
| temperature=0.7, | |
| max_tokens=500 | |
| ) | |
| return response.choices[0].message.content | |
| except Exception as e: | |
| return f"Error: {str(e)}" | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # HELPER FUNCTIONS | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def get_weather_data(location: str) -> Optional[Dict]: | |
| """Get weather from OpenWeatherMap""" | |
| try: | |
| api_key = None | |
| try: | |
| if hasattr(st, 'secrets') and "OPENWEATHER_API_KEY" in st.secrets: | |
| api_key = st.secrets["OPENWEATHER_API_KEY"] | |
| except: | |
| pass | |
| if not api_key: | |
| api_key = os.environ.get("OPENWEATHER_API_KEY", "") | |
| if not api_key: | |
| return None | |
| geo_url = "https://api.openweathermap.org/geo/1.0/direct" | |
| geo_params = {"q": location, "limit": 1, "appid": api_key} | |
| geo_resp = requests.get(geo_url, params=geo_params, timeout=5) | |
| if not geo_resp.json(): | |
| return None | |
| lat, lon = geo_resp.json()[0]['lat'], geo_resp.json()[0]['lon'] | |
| weather_url = "https://api.openweathermap.org/data/2.5/weather" | |
| weather_params = {"lat": lat, "lon": lon, "appid": api_key, "units": "metric"} | |
| weather_resp = requests.get(weather_url, params=weather_params, timeout=5) | |
| data = weather_resp.json() | |
| return { | |
| 'temperature': data['main']['temp'], | |
| 'humidity': data['main']['humidity'], | |
| 'pressure': data['main']['pressure'], | |
| 'wind_speed': data['wind']['speed'], | |
| 'description': data['weather'][0]['description'], | |
| 'location': data['name'] | |
| } | |
| except: | |
| return None | |
| def get_current_season() -> str: | |
| """Get current agricultural season""" | |
| month = datetime.now().month | |
| if month in [6, 7, 8, 9]: | |
| return "Kharif" | |
| elif month in [10, 11, 12, 1, 2]: | |
| return "Rabi" | |
| else: | |
| return "Summer" | |
| def get_market_prices(crop: str) -> Dict: | |
| """Get market prices""" | |
| prices = { | |
| "Wheat": 2200, "Rice": 2500, "Cotton": 5500, "Sugarcane": 290, | |
| "Potato": 1500, "Tomato": 1200, "Onion": 1800, "Corn": 1900 | |
| } | |
| base = prices.get(crop, 2000) | |
| return {'crop': crop, 'price': base, 'min': base * 0.85, 'max': base * 1.15} | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # SESSION STATE INITIALIZATION | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| if "messages" not in st.session_state: | |
| st.session_state.messages = [] | |
| if "location" not in st.session_state: | |
| st.session_state.location = "Maharashtra" | |
| if "language" not in st.session_state: | |
| st.session_state.language = "English" | |
| if "openai_client" not in st.session_state: | |
| client, error = initialize_openai() | |
| st.session_state.openai_client = client | |
| st.session_state.openai_error = error | |
| if "user_authenticated" not in st.session_state: | |
| st.session_state.user_authenticated = False | |
| st.session_state.username = None | |
| if 'db_initialized' not in st.session_state: | |
| init_farm_database() | |
| init_soil_database() | |
| init_user_database() | |
| st.session_state.db_initialized = True | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # SIDEBAR | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| with st.sidebar: | |
| st.markdown("### โ๏ธ SETTINGS") | |
| location = st.selectbox( | |
| "๐ Your Location", | |
| ["Maharashtra", "Punjab", "Haryana", "Uttar Pradesh", "Karnataka"], | |
| key="sidebar_location" | |
| ) | |
| st.session_state.location = location | |
| language = st.selectbox( | |
| "๐ Language", | |
| ["English", "Hindi", "Marathi"], | |
| key="sidebar_language" | |
| ) | |
| st.session_state.language = language | |
| st.divider() | |
| st.markdown("### ๐ค AI STATUS") | |
| if st.session_state.openai_client: | |
| st.success("โ AI Enabled") | |
| else: | |
| st.error("โ AI Disabled") | |
| if st.button("๐ Reinitialize AI"): | |
| client, error = initialize_openai() | |
| st.session_state.openai_client = client | |
| st.session_state.openai_error = error | |
| st.rerun() | |
| st.divider() | |
| display_alerts() | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # MAIN CONTENT | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| st.markdown("# ๐พ FARMER COPILOT v3.0 - COMPLETE") | |
| st.markdown("### AI Agricultural Intelligence Platform with 15 Features ๐") | |
| st.divider() | |
| # DEFINE TABS WITH UNIQUE KEYS | |
| tab1, tab2, tab3, tab4, tab5, tab6, tab7, tab8, tab9, tab10, tab11, tab12 = st.tabs([ | |
| "๐ฌ AI Chat", "๐ค๏ธ Weather", "๐ฐ Market", "๐ฑ Crops", | |
| "๐ Pests", "๐ง Irrigation", "๐ Analytics", "๐ธ Image", | |
| "๐ค Voice", "๐งช Soil", "๐ Yield", "๐ค Profile" | |
| ]) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # TAB 1: AI CHAT | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| with tab1: | |
| st.markdown("### ๐ฌ Talk to Your AI Copilot") | |
| if not st.session_state.openai_client: | |
| st.warning("โ ๏ธ AI is disabled! Add OPENAI_API_KEY to secrets.") | |
| user_input = st.text_input("Your question...", key="chat_input_main") | |
| if st.button("๐ Send", key="chat_send_btn"): | |
| if user_input: | |
| st.session_state.messages.append(("user", user_input)) | |
| if st.session_state.openai_client: | |
| context = {"location": st.session_state.location, "season": get_current_season()} | |
| with st.spinner("๐ค Thinking..."): | |
| ai_response = get_ai_response( | |
| st.session_state.openai_client, | |
| user_input, | |
| context, | |
| st.session_state.language | |
| ) | |
| else: | |
| ai_response = "AI is disabled." | |
| st.session_state.messages.append(("ai", ai_response)) | |
| st.rerun() | |
| if st.session_state.messages: | |
| for msg_type, content in st.session_state.messages[-10:]: | |
| if msg_type == "user": | |
| st.info(f"๐จโ๐พ You: {content}") | |
| else: | |
| st.success(f"๐ค Copilot: {content}") | |
| if st.button("๐๏ธ Clear Chat", key="clear_chat_btn"): | |
| st.session_state.messages = [] | |
| st.rerun() | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # TAB 2: WEATHER | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| with tab2: | |
| st.markdown("### ๐ค๏ธ Weather & Climate") | |
| if st.button("๐ Refresh Weather", key="weather_refresh"): | |
| with st.spinner("Fetching..."): | |
| weather = get_weather_data(st.session_state.location) | |
| if weather: | |
| col1, col2, col3 = st.columns(3) | |
| col1.metric("๐ก๏ธ Temperature", f"{weather['temperature']:.1f}ยฐC") | |
| col2.metric("๐ง Humidity", f"{weather['humidity']}%") | |
| col3.metric("๐จ Wind", f"{weather['wind_speed']:.1f} m/s") | |
| if st.button("๐ Get 7-Day Forecast", key="weather_forecast"): | |
| forecast_df = get_7day_forecast(st.session_state.location) | |
| if forecast_df is not None: | |
| st.dataframe(forecast_df, use_container_width=True) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # TAB 3: MARKET PRICES | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| with tab3: | |
| st.markdown("### ๐ฐ Live Market Prices") | |
| if st.button("๐ Refresh Prices", key="market_refresh"): | |
| live_prices = get_live_market_prices() | |
| if live_prices: | |
| col1, col2 = st.columns(2) | |
| crops_list = list(live_prices.keys()) | |
| with col1: | |
| for crop in crops_list[:4]: | |
| st.metric(crop, f"โน{live_prices[crop]}") | |
| with col2: | |
| for crop in crops_list[4:]: | |
| st.metric(crop, f"โน{live_prices[crop]}") | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # TAB 4-7: PLACEHOLDER TABS (Crops, Pests, Irrigation, Analytics) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| with tab4: | |
| st.markdown("### ๐ฑ Crop Recommendations") | |
| st.info("๐พ Cotton, Wheat, Rice, Sugarcane - Select based on season") | |
| st.write("Current Season:", get_current_season()) | |
| with tab5: | |
| st.markdown("### ๐ Pest & Disease Management") | |
| st.info("Pest management tips and identification guide") | |
| with tab6: | |
| st.markdown("### ๐ง Irrigation Management") | |
| st.info("Smart irrigation scheduling and water conservation") | |
| with tab7: | |
| st.markdown("### ๐ Farm Analytics") | |
| st.info("Profit calculations and farm statistics") | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # TAB 8: IMAGE RECOGNITION (UNIQUE KEYS!) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| with tab8: | |
| st.markdown("### ๐ธ Pest & Disease Detection") | |
| uploaded_file = st.file_uploader("Upload leaf photo", type=['jpg', 'jpeg', 'png'], key="image_uploader") | |
| if uploaded_file and st.button("๐ Analyze", key="image_analyze_btn"): | |
| if IMAGE_RECOGNITION_AVAILABLE: | |
| result, error = analyze_plant_disease(uploaded_file) | |
| if error: | |
| st.error(error) | |
| else: | |
| col1, col2, col3 = st.columns(3) | |
| col1.metric("Disease", result['disease'].split('-')[0]) | |
| col2.metric("Confidence", f"{result['confidence']:.1f}%") | |
| col3.metric("Severity", result['severity']) | |
| else: | |
| st.warning("Image recognition not available") | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # TAB 9: VOICE (UNIQUE KEYS!) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| with tab9: | |
| st.markdown("### ๐ค Voice Interaction") | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| if st.button("๐๏ธ Speak Question", key="voice_input_btn"): | |
| if VOICE_AVAILABLE: | |
| text, error = voice_input() | |
| if error: | |
| st.error(error) | |
| elif text: | |
| st.success(f"You said: {text}") | |
| else: | |
| st.warning("Voice not available") | |
| with col2: | |
| if st.button("๐ Play Response", key="voice_output_btn"): | |
| if VOICE_AVAILABLE and st.session_state.messages: | |
| last_response = st.session_state.messages[-1][1] | |
| voice_output(last_response) | |
| else: | |
| st.warning("No response to play") | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # TAB 10: SOIL HEALTH (UNIQUE KEYS!) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| with tab10: | |
| st.markdown("### ๐งช Soil Health Monitoring") | |
| col1, col2, col3 = st.columns(3) | |
| with col1: | |
| pH = st.slider("Soil pH", 4.0, 9.0, 6.5, key="soil_pH_slider") | |
| nitrogen = st.slider("Nitrogen (mg/kg)", 0, 1000, 250, key="soil_nitrogen_slider") | |
| with col2: | |
| phosphorus = st.slider("Phosphorus (mg/kg)", 0, 100, 20, key="soil_phosphorus_slider") | |
| potassium = st.slider("Potassium (mg/kg)", 0, 500, 150, key="soil_potassium_slider") | |
| with col3: | |
| organic_matter = st.slider("Organic Matter (%)", 0.0, 10.0, 2.5, key="soil_organic_slider") | |
| moisture = st.slider("Soil Moisture (%)", 0.0, 50.0, 25.0, key="soil_moisture_slider") | |
| if st.button("๐พ Save Soil Test", key="soil_save_btn"): | |
| save_soil_test(st.session_state.location, pH, nitrogen, phosphorus, potassium, organic_matter, moisture) | |
| st.success("Saved!") | |
| recommendations = get_soil_recommendations(pH, nitrogen, phosphorus, potassium) | |
| for rec in recommendations: | |
| st.markdown(rec) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # TAB 11: YIELD PREDICTION (UNIQUE KEYS!) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| with tab11: | |
| st.markdown("### ๐ Yield Prediction") | |
| col1, col2, col3 = st.columns(3) | |
| with col1: | |
| temp = st.slider("Temperature (ยฐC)", 0, 45, 25, key="yield_temp_slider") | |
| humidity = st.slider("Humidity (%)", 0, 100, 70, key="yield_humidity_slider") | |
| with col2: | |
| rainfall = st.slider("Rainfall (mm)", 0, 500, 250, key="yield_rainfall_slider") | |
| pH = st.slider("Soil pH", 4.0, 9.0, 6.8, key="yield_pH_slider") | |
| with col3: | |
| org_matter = st.slider("Organic Matter (%)", 0.0, 10.0, 2.5, key="yield_orgmatter_slider") | |
| if st.button("๐ฎ Predict Yield", key="yield_predict_btn"): | |
| yield_pred = predict_yield(temp, humidity, rainfall, pH, org_matter) | |
| st.metric("Predicted Yield", f"{yield_pred:.1f} q/hectare") | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # TAB 12: USER PROFILE (UNIQUE KEYS!) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| with tab12: | |
| st.markdown("### ๐ค User Profile & Settings") | |
| if not st.session_state.user_authenticated: | |
| auth_choice = st.radio("Choose", ["๐ Login", "๐ Register"], key="auth_choice_radio") | |
| if auth_choice == "๐ Login": | |
| username = st.text_input("Username", key="login_username") | |
| password = st.text_input("Password", type="password", key="login_password") | |
| if st.button("Login", key="login_btn"): | |
| success, result = login_user(username, password) | |
| if success: | |
| st.session_state.user_authenticated = True | |
| st.session_state.username = username | |
| st.success("Logged in!") | |
| st.rerun() | |
| else: | |
| st.error("Invalid credentials") | |
| else: | |
| new_username = st.text_input("Username", key="register_username") | |
| new_email = st.text_input("Email", key="register_email") | |
| new_password = st.text_input("Password", type="password", key="register_password") | |
| if st.button("Register", key="register_btn"): | |
| success, msg = register_user(new_username, new_password, new_email, st.session_state.location) | |
| st.success(msg) if success else st.error(msg) | |
| else: | |
| st.success(f"Logged in as: {st.session_state.username}") | |
| if st.button("Logout", key="logout_btn"): | |
| st.session_state.user_authenticated = False | |
| st.rerun() | |
| st.divider() | |
| st.markdown("<div style='text-align: center'><p>๐พ FARMER COPILOT v3.0 - All 15 Features | Powered by OpenAI</p></div>", unsafe_allow_html=True) |