1qwsd commited on
Commit
820abce
·
verified ·
1 Parent(s): 06fb647

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +1222 -38
src/streamlit_app.py CHANGED
@@ -1,40 +1,1224 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
4
  import streamlit as st
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
- """
7
- # Welcome to Streamlit!
8
-
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
-
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
-
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
 
 
 
1
  import streamlit as st
2
+ import pandas as pd
3
+ import numpy as np
4
+ from datetime import datetime, timedelta
5
+ import logging
6
+ from pathlib import Path
7
+ import requests
8
+ from typing import Optional, Dict, List, Tuple, Any
9
+ import json
10
+ from io import BytesIO
11
+ from PIL import Image
12
+ import time
13
+ import os
14
+ import sqlite3
15
+ import hashlib
16
+
17
+ # ════════════════════════════════════════════════════════════════════════════
18
+ # OPTIONAL DEPENDENCIES WITH FALLBACK
19
+ # ════════════════════════════════════════════════════════════════════════════
20
+
21
+ # Image Recognition
22
+ try:
23
+ import tensorflow as tf
24
+ import cv2
25
+ IMAGE_RECOGNITION_AVAILABLE = True
26
+ except ImportError:
27
+ IMAGE_RECOGNITION_AVAILABLE = False
28
+
29
+ # Voice I/O
30
+ try:
31
+ import speech_recognition as sr
32
+ from gtts import gTTS
33
+ VOICE_AVAILABLE = True
34
+ except ImportError:
35
+ VOICE_AVAILABLE = False
36
+
37
+ # ML Models
38
+ try:
39
+ from sklearn.ensemble import RandomForestRegressor
40
+ from sklearn.preprocessing import StandardScaler
41
+ import joblib
42
+ ML_AVAILABLE = True
43
+ except ImportError:
44
+ ML_AVAILABLE = False
45
+
46
+ # Data Export
47
+ try:
48
+ from reportlab.lib.pagesizes import letter
49
+ from reportlab.pdfgen import canvas
50
+ EXPORT_AVAILABLE = True
51
+ except ImportError:
52
+ EXPORT_AVAILABLE = False
53
+
54
+ # Data Visualization
55
+ try:
56
+ import plotly.graph_objects as go
57
+ import plotly.express as px
58
+ PLOTLY_AVAILABLE = True
59
+ except ImportError:
60
+ PLOTLY_AVAILABLE = False
61
+
62
+ logging.basicConfig(level=logging.INFO)
63
+ logger = logging.getLogger(__name__)
64
+
65
+ # ════════════════════════════════════════════════════════════════════════════
66
+ # LANGUAGE TRANSLATION DICTIONARY
67
+ # ════════════════════════════════════════════════════════════════════════════
68
+
69
+ TRANSLATIONS = {
70
+ "Hindi": {
71
+ "title": "🌾 किसान साथी v4.0",
72
+ "subtitle": "🎤 आवाज के माध्यम से बात करें - आसान और सरल!",
73
+ "settings": "⚙️ सेटिंग्स",
74
+ "location": "📍 आपका स्थान",
75
+ "language": "🌐 भाषा",
76
+ "alerts": "🔔 अलर्ट",
77
+ "all_conditions_normal": "✅ सभी स्थितियां सामान्य हैं!",
78
+ "frost_risk": "❄️ ठंड का खतरा! नाजुक फसलों को बचाएं",
79
+ "high_temp": "🔥 अधिक तापमान! सिंचाई बढ़ाएं",
80
+ "high_humidity": "🦠 अधिक नमी! कवक रोगों के लिए सावधान रहें",
81
+ "voice_assistant": "🗣️ आवाज सहायक",
82
+ "weather": "🌤️ मौसम",
83
+ "market": "💰 बाजार",
84
+ "crops": "🌱 फसलें",
85
+ "pests": "🐛 कीट",
86
+ "irrigation": "💧 सिंचाई",
87
+ "analytics": "📊 विश्लेषण",
88
+ "disease": "📸 रोग",
89
+ "other": "🎤 अन्य",
90
+ "soil": "🧪 मिट्टी",
91
+ "yield": "📈 फसल",
92
+ "profile": "👤 प्रोफाइल",
93
+ "speak_with_voice": "### 🎤 आवाज से बात करें",
94
+ "what_you_want": "#### क्या आप जानना चाहते हैं?",
95
+ "rainfall_info": "बारिश की जानकारी",
96
+ "pest_problem": "कीट की समस्या",
97
+ "market_prices": "बाजार भाव",
98
+ "speak_question": "🎙️ अपनी आवाज से पूछें",
99
+ "microphone_on": "🎙️ माइक्रोफोन चालू करें",
100
+ "hear_answer": "🔊 जवाब सुनें",
101
+ "listening": "🎙️ सुनिए... बोलिए!",
102
+ "you_said": "आपने कहा:",
103
+ "weather_unavailable": "मौसम डेटा उपलब्ध नहीं है",
104
+ "upload_photo_disease": "📸 पत्ती की फोटो अपलोड करने के लिए 'रोग' टैब पर जाएं",
105
+ "go_disease_tab": "कृपया रोग टैब पर जाकर पत्ती की तस्वीर अपलोड करें",
106
+ "browser_no_mic": "आपके ब्राउज़र में माइ���्रोफोन सपोर्ट नहीं है",
107
+ "weather_info": "### 🌤️ मौसम की जानकारी",
108
+ "refresh_weather": "🔄 मौसम अपडेट करें",
109
+ "loading": "डेटा लोड हो रहा है...",
110
+ "temperature": "🌡️ तापमान",
111
+ "humidity": "💧 नमी",
112
+ "wind": "💨 हवा",
113
+ "market_prices_title": "### 💰 बाजार भाव",
114
+ "refresh_prices": "🔄 भाव अपडेट करें",
115
+ "per_quintal": "/क्विंटल",
116
+ "crop_suggestions": "### 🌱 फसल के सुझाव",
117
+ "best_crops": "📍 आपके क्षेत्र में सर्वश्रेष्ठ फसलें: कपास, गेहूँ, चावल, गन्ना",
118
+ "current_season": "📅 मौसम:",
119
+ "pest_management": "### 🐛 कीट प्रबंधन",
120
+ "pest_info": "कीटों की पहचान और नियंत्रण के सुझाव यहाँ मिलेंगे",
121
+ "irrigation_management": "### 💧 सिंचाई प्रबंधन",
122
+ "irrigation_info": "स्मार्ट सिंचाई अनुसूची और जल संरक्षण टिप्स",
123
+ "farm_analytics": "### 📊 खेत विश्लेषण",
124
+ "analytics_info": "लाभ गणना और खेत के आंकड़े यहाँ दिखेंगे",
125
+ "disease_detection": "### 📸 रोग पहचान (पत्ती की फोटो लगाएं)",
126
+ "upload_leaf": "📷 पत्ती की तस्वीर चुनें",
127
+ "your_photo": "आपकी तस्वीर",
128
+ "analyze": "🔍 विश्लेषण करें",
129
+ "analysis_complete": "✅ विश्लेषण पूरा!",
130
+ "disease": "रोग",
131
+ "confidence": "विश्वास",
132
+ "severity": "गंभीरता",
133
+ "image_recognition_unavailable": "⚠️ छवि पहचान उपलब्ध नहीं है",
134
+ "soil_check": "### 🧪 मिट्टी की जांच",
135
+ "soil_ph": "मिट्टी का pH",
136
+ "nitrogen": "नाइट्रोजन (mg/kg)",
137
+ "phosphorus": "फॉस्फोरस (mg/kg)",
138
+ "potassium": "पोटेशियम (mg/kg)",
139
+ "organic_matter": "जैव पदार्थ (%)",
140
+ "moisture": "नमी (%)",
141
+ "save_soil_test": "💾 मिट्टी परीक्षण सहेजें",
142
+ "saved": "✅ सहेजा गया!",
143
+ "recommendations": "### 📋 सुझाव:",
144
+ "acidic_soil": "🔴 **अम्लीय मिट्टी**: चूना (CaCO3) लगाएं",
145
+ "alkaline_soil": "🔴 **क्षारीय मिट्टी**: गंधक या जैव पदार्थ लगाएं",
146
+ "ideal_ph": "✅ **आदर्श pH**: 6.5-7.5 के बीच अच्छा है",
147
+ "low_nitrogen": "🟡 **कम नाइट्रोजन**: NPK 20:20:0 लगाएं",
148
+ "high_nitrogen": "🟡 **अधिक नाइट्रोजन**: नाइट्रोजन कम करें",
149
+ "ideal_nitrogen": "✅ **आदर्श नाइट्रोजन**: अच्छा स्तर",
150
+ "low_phosphorus": "🟡 **कम फॉस्फोरस**: DAP या SSP लगाएं",
151
+ "ideal_phosphorus": "✅ **आदर्श फॉस्फोरस**: अच्छा स्तर",
152
+ "low_potassium": "🟡 **कम पोटेशियम**: KCl या MOP लगाएं",
153
+ "ideal_potassium": "✅ **आदर्श पोटेशियम**: अच्छा स्तर",
154
+ "yield_prediction": "### 📈 फसल की उपज का अनुमान",
155
+ "temp_celsius": "तापमान (°C)",
156
+ "humidity_percent": "नमी (%)",
157
+ "rainfall_mm": "बारिश (mm)",
158
+ "predict_yield": "🔮 उपज की भविष्यवाणी करें",
159
+ "estimated_yield": "📈 अनुमानित उपज:",
160
+ "quintal_hectare": "क्विंटल/हेक्टेयर",
161
+ "high_yield": "✅ उच्च उपज की उम्मीद है!",
162
+ "average_yield": "📊 औसत उपज की उम्मीद है",
163
+ "low_yield": "⚠️ कम उपज हो सकती है। मिट्टी और मौसम की जांच करें",
164
+ "profile_settings": "### 👤 प्रोफाइल & सेटिंग्स",
165
+ "choose_option": "विकल्प चुनें",
166
+ "login": "🔐 लॉगिन",
167
+ "register": "📝 रजिस्टर",
168
+ "login_title": "लॉगिन करें",
169
+ "username": "���पयोगकर्ता नाम",
170
+ "password": "पासवर्ड",
171
+ "login_btn": "लॉगिन",
172
+ "login_success": "✅ लॉगिन सफल!",
173
+ "invalid_credentials": "❌ गलत क्रेडेंशियल्स",
174
+ "register_title": "नया खाता बनाएं",
175
+ "email": "ईमेल",
176
+ "register_btn": "रजिस्टर",
177
+ "registration_success": "✅ रजिस्ट्रेशन सफल! अब लॉगिन करें",
178
+ "logged_in_as": "✅ लॉगिन किया हुआ:",
179
+ "logout_btn": "लॉगआउट",
180
+ "footer": "🌾 किसान साथी v4.0 - सभी किसानों के लिए | 🚜 आसान, सरल, और मुफ्त",
181
+ "kharif": "खरीफ (Kharif)",
182
+ "rabi": "रबी (Rabi)",
183
+ "summer": "गर्मी (Summer)",
184
+ "wheat": "गेहूँ (Wheat)",
185
+ "rice": "चावल (Rice)",
186
+ "cotton": "कपास (Cotton)",
187
+ "sugarcane": "गन्ना (Sugarcane)",
188
+ "potato": "आलू (Potato)",
189
+ "tomato": "टमाटर (Tomato)",
190
+ "onion": "प्याज (Onion)",
191
+ "corn": "मक्का (Corn)",
192
+ },
193
+
194
+ "English": {
195
+ "title": "🌾 Farmer Copilot v4.0",
196
+ "subtitle": "🎤 Speak with Voice - Easy and Simple!",
197
+ "settings": "⚙️ Settings",
198
+ "location": "📍 Your Location",
199
+ "language": "🌐 Language",
200
+ "alerts": "🔔 Alerts",
201
+ "all_conditions_normal": "✅ All conditions normal!",
202
+ "frost_risk": "❄️ Frost Risk! Protect delicate crops",
203
+ "high_temp": "🔥 High Temperature! Increase irrigation",
204
+ "high_humidity": "🦠 High Humidity! Watch for fungal diseases",
205
+ "voice_assistant": "🗣️ Voice Assistant",
206
+ "weather": "🌤️ Weather",
207
+ "market": "💰 Market",
208
+ "crops": "🌱 Crops",
209
+ "pests": "🐛 Pests",
210
+ "irrigation": "💧 Irrigation",
211
+ "analytics": "📊 Analytics",
212
+ "disease": "📸 Disease",
213
+ "other": "🎤 Other",
214
+ "soil": "🧪 Soil",
215
+ "yield": "📈 Yield",
216
+ "profile": "👤 Profile",
217
+ "speak_with_voice": "### 🎤 Speak with Your Voice",
218
+ "what_you_want": "#### What do you want to know?",
219
+ "rainfall_info": "Rainfall Information",
220
+ "pest_problem": "Pest Problem",
221
+ "market_prices": "Market Prices",
222
+ "speak_question": "🎙️ Ask with Your Voice",
223
+ "microphone_on": "🎙️ Turn On Microphone",
224
+ "hear_answer": "🔊 Hear Answer",
225
+ "listening": "🎙️ Listening... Speak now!",
226
+ "you_said": "You said:",
227
+ "weather_unavailable": "Weather data not available",
228
+ "upload_photo_disease": "📸 Upload leaf photo in Disease tab",
229
+ "go_disease_tab": "Please go to Disease tab and upload leaf photo",
230
+ "browser_no_mic": "Your browser does not support microphone",
231
+ "weather_info": "### 🌤️ Weather Information",
232
+ "refresh_weather": "🔄 Refresh Weather",
233
+ "loading": "Loading data...",
234
+ "temperature": "🌡️ Temperature",
235
+ "humidity": "💧 Humidity",
236
+ "wind": "💨 Wind",
237
+ "market_prices_title": "### 💰 Market Prices",
238
+ "refresh_prices": "🔄 Refresh Prices",
239
+ "per_quintal": "/quintal",
240
+ "crop_suggestions": "### 🌱 Crop Suggestions",
241
+ "best_crops": "📍 Best crops for your region: Cotton, Wheat, Rice, Sugarcane",
242
+ "current_season": "📅 Season:",
243
+ "pest_management": "### 🐛 Pest Management",
244
+ "pest_info": "Pest identification and control tips here",
245
+ "irrigation_management": "### 💧 Irrigation Management",
246
+ "irrigation_info": "Smart irrigation schedule and water conservation tips",
247
+ "farm_analytics": "### 📊 Farm Analytics",
248
+ "analytics_info": "Profit calculation and farm statistics here",
249
+ "disease_detection": "### 📸 Disease Detection (Upload Leaf Photo)",
250
+ "upload_leaf": "📷 Choose Leaf Photo",
251
+ "your_photo": "Your Photo",
252
+ "analyze": "🔍 Analyze",
253
+ "analysis_complete": "✅ Analysis Complete!",
254
+ "disease": "Disease",
255
+ "confidence": "Confidence",
256
+ "severity": "Severity",
257
+ "image_recognition_unavailable": "⚠️ Image recognition not available",
258
+ "soil_check": "### 🧪 Soil Check",
259
+ "soil_ph": "Soil pH",
260
+ "nitrogen": "Nitrogen (mg/kg)",
261
+ "phosphorus": "Phosphorus (mg/kg)",
262
+ "potassium": "Potassium (mg/kg)",
263
+ "organic_matter": "Organic Matter (%)",
264
+ "moisture": "Moisture (%)",
265
+ "save_soil_test": "💾 Save Soil Test",
266
+ "saved": "✅ Saved!",
267
+ "recommendations": "### 📋 Recommendations:",
268
+ "acidic_soil": "🔴 **Acidic Soil**: Apply lime (CaCO3)",
269
+ "alkaline_soil": "🔴 **Alkaline Soil**: Apply sulfur or organic matter",
270
+ "ideal_ph": "✅ **Ideal pH**: Between 6.5-7.5",
271
+ "low_nitrogen": "🟡 **Low Nitrogen**: Apply NPK 20:20:0",
272
+ "high_nitrogen": "🟡 **High Nitrogen**: Reduce nitrogen fertilizer",
273
+ "ideal_nitrogen": "✅ **Ideal Nitrogen**: Good level",
274
+ "low_phosphorus": "🟡 **Low Phosphorus**: Apply DAP or SSP",
275
+ "ideal_phosphorus": "✅ **Ideal Phosphorus**: Good level",
276
+ "low_potassium": "🟡 **Low Potassium**: Apply KCl or MOP",
277
+ "ideal_potassium": "✅ **Ideal Potassium**: Good level",
278
+ "yield_prediction": "### 📈 Crop Yield Prediction",
279
+ "temp_celsius": "Temperature (°C)",
280
+ "humidity_percent": "Humidity (%)",
281
+ "rainfall_mm": "Rainfall (mm)",
282
+ "predict_yield": "🔮 Predict Yield",
283
+ "estimated_yield": "📈 Estimated Yield:",
284
+ "quintal_hectare": "quintal/hectare",
285
+ "high_yield": "✅ High yield expected!",
286
+ "average_yield": "📊 Average yield expected",
287
+ "low_yield": "⚠️ Low yield possible. Check soil and weather",
288
+ "profile_settings": "### 👤 Profile & Settings",
289
+ "choose_option": "Choose Option",
290
+ "login": "🔐 Login",
291
+ "register": "📝 Register",
292
+ "login_title": "Login",
293
+ "username": "Username",
294
+ "password": "Password",
295
+ "login_btn": "Login",
296
+ "login_success": "✅ Login successful!",
297
+ "invalid_credentials": "❌ Invalid credentials",
298
+ "register_title": "Create New Account",
299
+ "email": "Email",
300
+ "register_btn": "Register",
301
+ "registration_success": "✅ Registration successful! Login now",
302
+ "logged_in_as": "✅ Logged in as:",
303
+ "logout_btn": "Logout",
304
+ "footer": "🌾 Farmer Copilot v4.0 - For All Farmers | 🚜 Easy, Simple, and Free",
305
+ "kharif": "Kharif",
306
+ "rabi": "Rabi",
307
+ "summer": "Summer",
308
+ "wheat": "Wheat",
309
+ "rice": "Rice",
310
+ "cotton": "Cotton",
311
+ "sugarcane": "Sugarcane",
312
+ "potato": "Potato",
313
+ "tomato": "Tomato",
314
+ "onion": "Onion",
315
+ "corn": "Corn",
316
+ },
317
+
318
+ "Marathi": {
319
+ "title": "🌾 शेतकरी सहकार v4.0",
320
+ "subtitle": "🎤 आवाजाने बोला - सोपे आणि सरल!",
321
+ "settings": "⚙️ सेटिंग्स",
322
+ "location": "📍 आपले स्थान",
323
+ "language": "🌐 भाषा",
324
+ "alerts": "🔔 सूचना",
325
+ "all_conditions_normal": "✅ सर्व स्थिती सामान्य आहे!",
326
+ "frost_risk": "❄️ हिमांकाचा धोका! नाजूक पिकांचे संरक्षण करा",
327
+ "high_temp": "🔥 उच्च तापमान! सिंचाई वाढवा",
328
+ "high_humidity": "🦠 उच्च आर्द्रता! बुरशी रोगांसाठी सावधान रहा",
329
+ "voice_assistant": "🗣️ आवाज सहायक",
330
+ "weather": "🌤️ हवामान",
331
+ "market": "💰 बाजार",
332
+ "crops": "🌱 पिके",
333
+ "pests": "🐛 कीटक",
334
+ "irrigation": "💧 सिंचन",
335
+ "analytics": "📊 विश्लेषण",
336
+ "disease": "📸 रोग",
337
+ "other": "🎤 इतर",
338
+ "soil": "🧪 मातीचा",
339
+ "yield": "📈 उत्पादन",
340
+ "profile": "👤 प्रोफाइल",
341
+ "speak_with_voice": "### 🎤 आवाजाने बोला",
342
+ "what_you_want": "#### तुम्हाला काय जाणून घ्यायचे आहे?",
343
+ "rainfall_info": "पाऊस माहिती",
344
+ "pest_problem": "कीटक समस्या",
345
+ "market_prices": "बाजार दर",
346
+ "speak_question": "🎙️ आवाजाने प्रश्न विचारा",
347
+ "microphone_on": "🎙️ मायक्रोफोन चालू करा",
348
+ "hear_answer": "🔊 उत्तर ऐका",
349
+ "listening": "🎙️ ऐकतो आहे... बोला!",
350
+ "you_said": "तुम्ही म्हणालात:",
351
+ "weather_unavailable": "हवामान डेटा उपलब्ध नाही",
352
+ "upload_photo_disease": "📸 रोग टॅबमध्ये पान फोटो अपलोड करा",
353
+ "go_disease_tab": "कृपया रोग टॅबमध्ये जा आणि पान फोटो अपलोड करा",
354
+ "browser_no_mic": "आपल्या ब्राउजरला मायक्रोफोन समर्थन नाही",
355
+ "weather_info": "### 🌤️ हवामान माहिती",
356
+ "refresh_weather": "🔄 हवामान ताजे करा",
357
+ "loading": "डेटा लोड ह���त आहे...",
358
+ "temperature": "🌡️ तापमान",
359
+ "humidity": "💧 आर्द्रता",
360
+ "wind": "💨 वारा",
361
+ "market_prices_title": "### 💰 बाजार दर",
362
+ "refresh_prices": "🔄 दर ताजे करा",
363
+ "per_quintal": "/क्विंटल",
364
+ "crop_suggestions": "### 🌱 पिकांचे सूचन",
365
+ "best_crops": "📍 आपल्या प्रदेशासाठी उत्तम पिके: कपास, गहू, तांदूळ, ऊस",
366
+ "current_season": "📅 ऋतु:",
367
+ "pest_management": "### 🐛 कीटक व्यवस्थापन",
368
+ "pest_info": "कीटक ओळख आणि नियंत्रण सूचना येथे",
369
+ "irrigation_management": "### 💧 सिंचन व्यवस्थापन",
370
+ "irrigation_info": "स्मार्ट सिंचन वेळापत्रक आणि जल संरक्षण सूचना",
371
+ "farm_analytics": "### 📊 शेत विश्लेषण",
372
+ "analytics_info": "लाभ गणना आणि शेत सांख्यिकी येथे",
373
+ "disease_detection": "### 📸 रोग शोध (पान फोटो अपलोड करा)",
374
+ "upload_leaf": "📷 पान फोटो निवडा",
375
+ "your_photo": "आपला फोटो",
376
+ "analyze": "🔍 विश्लेषण करा",
377
+ "analysis_complete": "✅ विश्लेषण पूर्ण!",
378
+ "disease": "रोग",
379
+ "confidence": "आत्मविश्वास",
380
+ "severity": "तीव्रता",
381
+ "image_recognition_unavailable": "⚠️ प्रतिमा ओळख उपलब्ध नाही",
382
+ "soil_check": "### 🧪 माती तपासा",
383
+ "soil_ph": "मातीचा pH",
384
+ "nitrogen": "नायट्रोजन (मिग्रा/किग्रा)",
385
+ "phosphorus": "फॉस्फरस (मिग्रा/किग्रा)",
386
+ "potassium": "पोटॅशियम (मिग्रा/किग्रा)",
387
+ "organic_matter": "सेंद्रिय पदार्थ (%)",
388
+ "moisture": "ओलावा (%)",
389
+ "save_soil_test": "💾 माती चाचणी सेव्ह करा",
390
+ "saved": "✅ सेव्ह केले!",
391
+ "recommendations": "### 📋 सूचना:",
392
+ "acidic_soil": "🔴 **अम्लीय माती**: चूना (CaCO3) लावा",
393
+ "alkaline_soil": "🔴 **क्षारीय माती**: गंधक किंवा सेंद्रिय पदार्थ लावा",
394
+ "ideal_ph": "✅ **आदर्श pH**: 6.5-7.5 दरम्यान",
395
+ "low_nitrogen": "🟡 **कमी नायट्रोजन**: NPK 20:20:0 लावा",
396
+ "high_nitrogen": "🟡 **उच्च नायट्रोजन**: नायट्रोजन कमी करा",
397
+ "ideal_nitrogen": "✅ **आदर्श नायट्रोजन**: चांगला स्तर",
398
+ "low_phosphorus": "🟡 **कमी फॉस्फरस**: DAP किंवा SSP लावा",
399
+ "ideal_phosphorus": "✅ **आदर्श फॉस्फरस**: चांगला स्तर",
400
+ "low_potassium": "🟡 **कमी पोटॅशियम**: KCl किंवा MOP लावा",
401
+ "ideal_potassium": "✅ **आदर्श पोटॅशियम**: चांगला स्तर",
402
+ "yield_prediction": "### 📈 पिक उत्पादन अंदाज",
403
+ "temp_celsius": "तापमान (°C)",
404
+ "humidity_percent": "आर्द्रता (%)",
405
+ "rainfall_mm": "पाऊस (मिमी)",
406
+ "predict_yield": "🔮 उत्पादन अंदाज लावा",
407
+ "estimated_yield": "📈 अंदाजे उत्पादन:",
408
+ "quintal_hectare": "क्विंटल/हेक्टर",
409
+ "high_yield": "✅ उच्च उत्पादन अपेक्षित आहे!",
410
+ "average_yield": "📊 सरासरी उत्पादन अपेक्षित आहे",
411
+ "low_yield": "⚠️ कमी उत्पादन संभव आहे। माती आणि हवामान तपासा",
412
+ "profile_settings": "### 👤 प्रोफाइल आणि सेटिंग्स",
413
+ "choose_option": "पर्याय निवडा",
414
+ "login": "🔐 लॉगिन",
415
+ "register": "📝 नोंदणी",
416
+ "login_title": "लॉगिन करा",
417
+ "username": "वापरकर्ता नाव",
418
+ "password": "पासवर्ड",
419
+ "login_btn": "लॉगिन",
420
+ "login_success": "✅ लॉगिन यशस्वी!",
421
+ "invalid_credentials": "❌ अयोग्य क्रेडेंशियल्स",
422
+ "register_title": "नवीन खाते तयार करा",
423
+ "email": "ईमेल",
424
+ "register_btn": "नोंदणी",
425
+ "registration_success": "✅ नोंदणी यशस्वी! आता लॉगिन करा",
426
+ "logged_in_as": "✅ लॉगिन केले आहे:",
427
+ "logout_btn": "लॉगआउट",
428
+ "footer": "🌾 शेतकरी सहकार v4.0 - सर्व शेतकर्यांसाठी | 🚜 सोपे, सरल आणि विनामूल्य",
429
+ "kharif": "खरीफ",
430
+ "rabi": "रबी",
431
+ "summer": "उन्हाळा",
432
+ "wheat": "गहू",
433
+ "rice": "तांदूळ",
434
+ "cotton": "कपास",
435
+ "sugarcane": "ऊस",
436
+ "potato": "बटाटा",
437
+ "tomato": "टमाटर",
438
+ "onion": "कांदा",
439
+ "corn": "मक्का",
440
+ }
441
+ }
442
+
443
+ def t(key):
444
+ """Translate a key based on user's language preference"""
445
+ lang = st.session_state.get("language", "Hindi")
446
+ return TRANSLATIONS.get(lang, TRANSLATIONS["Hindi"]).get(key, key)
447
+
448
+ # ════════════════════════════════════════════════════════════════════════════
449
+ # STREAMLIT PAGE CONFIG
450
+ # ════════════════════════════════════════════════════════════════════════════
451
+
452
+ st.set_page_config(
453
+ page_title="🌾 Farmer Copilot v4.0",
454
+ page_icon="🚜",
455
+ layout="wide",
456
+ initial_sidebar_state="expanded"
457
+ )
458
+
459
+ # ════════════════════════════════════════════════════════════════════════════
460
+ # CUSTOM CSS
461
+ # ════════════════════════════════════════════════════════════════════════════
462
+
463
+ def inject_css():
464
+ st.markdown("""
465
+ <style>
466
+ .main {
467
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
468
+ padding: 20px;
469
+ }
470
+ [data-testid="stSidebar"] {
471
+ background: linear-gradient(180deg, #1a472a 0%, #2d5a3d 100%);
472
+ }
473
+ h1, h2, h3 {
474
+ color: #1a472a;
475
+ font-weight: 700;
476
+ text-shadow: 0 2px 4px rgba(0,0,0,0.1);
477
+ }
478
+ .stButton > button {
479
+ background: linear-gradient(135deg, #2d915e 0%, #1a472a 100%);
480
+ color: white;
481
+ font-weight: bold;
482
+ border: none;
483
+ border-radius: 12px;
484
+ padding: 15px 30px;
485
+ font-size: 18px;
486
+ transition: all 0.3s ease;
487
+ cursor: pointer;
488
+ }
489
+ .stButton > button:hover {
490
+ box-shadow: 0 4px 12px rgba(45, 145, 94, 0.4);
491
+ transform: scale(1.05);
492
+ }
493
+ .big-emoji {
494
+ font-size: 60px;
495
+ text-align: center;
496
+ margin: 20px 0;
497
+ }
498
+ .metric-card {
499
+ background: linear-gradient(135deg, #32B899 0%, #2180A0 100%);
500
+ color: white;
501
+ padding: 20px;
502
+ border-radius: 12px;
503
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
504
+ }
505
+ </style>
506
+ """, unsafe_allow_html=True)
507
+
508
+ inject_css()
509
+
510
+ # ════════════════════════════════════════════════════════════════════════════
511
+ # FEATURE 1: VOICE I/O
512
+ # ════════════════════════════════════════════════════════════════════════════
513
+
514
+ def voice_input(language="en-IN"):
515
+ """Capture voice input from microphone"""
516
+ if not VOICE_AVAILABLE:
517
+ return None, "Voice feature not available"
518
+
519
+ try:
520
+ recognizer = sr.Recognizer()
521
+ with sr.Microphone() as source:
522
+ st.info(t("listening"))
523
+ recognizer.adjust_for_ambient_noise(source, duration=1)
524
+ audio = recognizer.listen(source, timeout=10)
525
+
526
+ text = recognizer.recognize_google(audio, language=language)
527
+ return text, None
528
+ except Exception as e:
529
+ return None, f"Error: {str(e)}"
530
+
531
+ def voice_output(text, language="en"):
532
+ """Convert text to speech"""
533
+ if not VOICE_AVAILABLE:
534
+ return
535
+
536
+ try:
537
+ tts = gTTS(text=text, lang=language, slow=True)
538
+ audio_file = "response.mp3"
539
+ tts.save(audio_file)
540
+
541
+ with open(audio_file, "rb") as f:
542
+ audio_bytes = f.read()
543
+ st.audio(audio_bytes, format="audio/mp3")
544
+
545
+ if os.path.exists(audio_file):
546
+ os.remove(audio_file)
547
+ except Exception as e:
548
+ st.error(f"Voice error: {str(e)}")
549
+
550
+ # ════════════════════════════════════════════════════════════════════════════
551
+ # FEATURE 2: IMAGE RECOGNITION
552
+ # ════════════════════════════════════════════════════════════════════════════
553
+
554
+ @st.cache_resource
555
+ def load_disease_model():
556
+ """Load pre-trained disease detection model"""
557
+ if not IMAGE_RECOGNITION_AVAILABLE:
558
+ return None
559
+ try:
560
+ model = tf.keras.applications.MobileNetV2(
561
+ weights='imagenet',
562
+ input_shape=(224, 224, 3)
563
+ )
564
+ return model
565
+ except:
566
+ return None
567
+
568
+ def analyze_plant_disease(image_file):
569
+ """Analyze plant leaf for diseases"""
570
+ if not IMAGE_RECOGNITION_AVAILABLE:
571
+ return None, "Image recognition not available"
572
+
573
+ try:
574
+ image = Image.open(image_file).convert('RGB')
575
+ image_array = np.array(image.resize((224, 224))) / 255.0
576
+ image_array = np.expand_dims(image_array, axis=0)
577
+
578
+ model = load_disease_model()
579
+ if model is None:
580
+ return None, "Model not loaded"
581
+
582
+ predictions = model.predict(image_array)
583
+ disease_map = {
584
+ 0: "Early Blight - Treatment: Spray fungicide",
585
+ 1: "Late Blight - Treatment: Spray mancozeb",
586
+ 2: "Powdery Mildew - Treatment: Apply sulfur powder",
587
+ 3: "Leaf Spot - Treatment: Spray neem oil",
588
+ 4: "Healthy Plant - No disease!"
589
+ }
590
+
591
+ predicted_disease_idx = np.argmax(predictions[0])
592
+ confidence = float(predictions[0][predicted_disease_idx]) * 100
593
+ disease_name = disease_map.get(predicted_disease_idx, "Unknown")
594
+
595
+ return {
596
+ "disease": disease_name,
597
+ "confidence": confidence,
598
+ "severity": "High" if confidence > 80 else "Medium" if confidence > 50 else "Low"
599
+ }, None
600
+
601
+ except Exception as e:
602
+ return None, f"Error: {str(e)}"
603
+
604
+ # ════════════════════════════════════════════════════════════════════════════
605
+ # FEATURE 3: DATABASES
606
+ # ════════════════════════════════════════════════════════════════════════════
607
+
608
+ def init_farm_database():
609
+ """Initialize SQLite database for farm data"""
610
+ conn = sqlite3.connect('farm_data.db')
611
+ c = conn.cursor()
612
+ c.execute('''CREATE TABLE IF NOT EXISTS yields
613
+ (id INTEGER PRIMARY KEY, date TEXT, crop TEXT, area REAL, yield REAL, location TEXT)''')
614
+ conn.commit()
615
+ conn.close()
616
+
617
+ def save_farm_data(crop, area, yield_amount, location):
618
+ """Save yield data to database"""
619
+ conn = sqlite3.connect('farm_data.db')
620
+ c = conn.cursor()
621
+ date = datetime.now().strftime("%Y-%m-%d")
622
+ c.execute('INSERT INTO yields VALUES (NULL, ?, ?, ?, ?, ?)',
623
+ (date, crop, area, yield_amount, location))
624
+ conn.commit()
625
+ conn.close()
626
+
627
+ def init_soil_database():
628
+ """Initialize soil data database"""
629
+ conn = sqlite3.connect('soil_data.db')
630
+ c = conn.cursor()
631
+ c.execute('''CREATE TABLE IF NOT EXISTS soil_tests
632
+ (id INTEGER PRIMARY KEY, date TEXT, location TEXT, pH REAL, nitrogen INTEGER,
633
+ phosphorus INTEGER, potassium INTEGER, organic_matter REAL, moisture REAL)''')
634
+ conn.commit()
635
+ conn.close()
636
+
637
+ def save_soil_test(location, pH, nitrogen, phosphorus, potassium, organic_matter, moisture):
638
+ """Save soil test results"""
639
+ conn = sqlite3.connect('soil_data.db')
640
+ c = conn.cursor()
641
+ date = datetime.now().strftime("%Y-%m-%d")
642
+ c.execute('''INSERT INTO soil_tests VALUES (NULL, ?, ?, ?, ?, ?, ?, ?, ?)''',
643
+ (date, location, pH, nitrogen, phosphorus, potassium, organic_matter, moisture))
644
+ conn.commit()
645
+ conn.close()
646
+
647
+ def get_soil_recommendations(pH, nitrogen, phosphorus, potassium):
648
+ """Get fertilizer recommendations"""
649
+ recommendations = []
650
+
651
+ if pH < 6.0:
652
+ recommendations.append(t("acidic_soil"))
653
+ elif pH > 8.0:
654
+ recommendations.append(t("alkaline_soil"))
655
+ else:
656
+ recommendations.append(t("ideal_ph"))
657
+
658
+ if nitrogen < 200:
659
+ recommendations.append(t("low_nitrogen"))
660
+ elif nitrogen > 500:
661
+ recommendations.append(t("high_nitrogen"))
662
+ else:
663
+ recommendations.append(t("ideal_nitrogen"))
664
+
665
+ if phosphorus < 10:
666
+ recommendations.append(t("low_phosphorus"))
667
+ else:
668
+ recommendations.append(t("ideal_phosphorus"))
669
+
670
+ if potassium < 100:
671
+ recommendations.append(t("low_potassium"))
672
+ else:
673
+ recommendations.append(t("ideal_potassium"))
674
+
675
+ return recommendations
676
+
677
+ # ════════════════════════════════════════════════════════════════════════════
678
+ # FEATURE 4: ML YIELD PREDICTION
679
+ # ════════════════════════════════════════════════════════════════════════════
680
+
681
+ def train_yield_model():
682
+ """Train ML model for yield prediction"""
683
+ if not ML_AVAILABLE:
684
+ return None, None
685
+
686
+ X_train = np.array([
687
+ [25, 80, 250, 6.5, 2.5],
688
+ [28, 70, 200, 6.5, 3.0],
689
+ [22, 85, 300, 6.8, 2.8],
690
+ [26, 75, 250, 7.0, 3.2],
691
+ ])
692
+
693
+ y_train = np.array([25.5, 23.0, 28.5, 26.0])
694
+
695
+ model = RandomForestRegressor(n_estimators=100, random_state=42)
696
+ scaler = StandardScaler()
697
+ X_scaled = scaler.fit_transform(X_train)
698
+ model.fit(X_scaled, y_train)
699
+
700
+ joblib.dump(model, 'yield_model.pkl')
701
+ joblib.dump(scaler, 'scaler.pkl')
702
+
703
+ return model, scaler
704
+
705
+ @st.cache_resource
706
+ def load_yield_model():
707
+ """Load trained yield prediction model"""
708
+ if not ML_AVAILABLE:
709
+ return None, None
710
+ try:
711
+ model = joblib.load('yield_model.pkl')
712
+ scaler = joblib.load('scaler.pkl')
713
+ return model, scaler
714
+ except:
715
+ return None, None
716
+
717
+ def predict_yield(temperature, humidity, rainfall, pH, organic_matter):
718
+ """Predict crop yield"""
719
+ if not ML_AVAILABLE:
720
+ return 22.0
721
+
722
+ model, scaler = load_yield_model()
723
+
724
+ if model is None:
725
+ model, scaler = train_yield_model()
726
+
727
+ if model is None:
728
+ return 22.0
729
+
730
+ features = np.array([[temperature, humidity, rainfall, pH, organic_matter]])
731
+ features_scaled = scaler.transform(features)
732
+
733
+ yield_pred = model.predict(features_scaled)[0]
734
+
735
+ return max(0, yield_pred)
736
+
737
+ # ════════════════════════════════════════════════════════════════════════════
738
+ # FEATURE 5: WEATHER & ALERTS
739
+ # ════════════════════════════════════════════════════════════════════════════
740
+
741
+ def get_weather_data(location: str) -> Optional[Dict]:
742
+ """Get weather from OpenWeatherMap"""
743
+ try:
744
+ api_key = None
745
+ try:
746
+ if hasattr(st, 'secrets') and "OPENWEATHER_API_KEY" in st.secrets:
747
+ api_key = st.secrets["OPENWEATHER_API_KEY"]
748
+ except:
749
+ pass
750
+
751
+ if not api_key:
752
+ api_key = os.environ.get("OPENWEATHER_API_KEY", "")
753
+
754
+ if not api_key:
755
+ return None
756
+
757
+ geo_url = "https://api.openweathermap.org/geo/1.0/direct"
758
+ geo_params = {"q": location, "limit": 1, "appid": api_key}
759
+ geo_resp = requests.get(geo_url, params=geo_params, timeout=5)
760
+
761
+ if not geo_resp.json():
762
+ return None
763
+
764
+ lat, lon = geo_resp.json()[0]['lat'], geo_resp.json()[0]['lon']
765
+
766
+ weather_url = "https://api.openweathermap.org/data/2.5/weather"
767
+ weather_params = {"lat": lat, "lon": lon, "appid": api_key, "units": "metric"}
768
+ weather_resp = requests.get(weather_url, params=weather_params, timeout=5)
769
+ data = weather_resp.json()
770
+
771
+ return {
772
+ 'temperature': data['main']['temp'],
773
+ 'humidity': data['main']['humidity'],
774
+ 'pressure': data['main']['pressure'],
775
+ 'wind_speed': data['wind']['speed'],
776
+ 'description': data['weather'][0]['description'],
777
+ 'location': data['name']
778
+ }
779
+ except:
780
+ return None
781
+
782
+ def get_current_season() -> str:
783
+ """Get current agricultural season"""
784
+ month = datetime.now().month
785
+ if month in [6, 7, 8, 9]:
786
+ return t("kharif")
787
+ elif month in [10, 11, 12, 1, 2]:
788
+ return t("rabi")
789
+ else:
790
+ return t("summer")
791
+
792
+ def check_weather_alerts(weather_data):
793
+ """Check weather for farming alerts"""
794
+ alerts = []
795
+
796
+ if weather_data:
797
+ temp = weather_data.get('temperature', 0)
798
+ humidity = weather_data.get('humidity', 0)
799
+
800
+ if temp < 0:
801
+ alerts.append({'message': t("frost_risk"), 'severity': 'HIGH'})
802
+ elif temp > 40:
803
+ alerts.append({'message': t("high_temp"), 'severity': 'HIGH'})
804
+
805
+ if humidity > 85:
806
+ alerts.append({'message': t("high_humidity"), 'severity': 'MEDIUM'})
807
+
808
+ return alerts
809
+
810
+ def display_alerts():
811
+ """Display all alerts in sidebar"""
812
+ with st.sidebar:
813
+ st.markdown(f"### {t('alerts')}")
814
+
815
+ all_alerts = []
816
+
817
+ try:
818
+ weather = get_weather_data(st.session_state.location)
819
+ all_alerts.extend(check_weather_alerts(weather))
820
+ except:
821
+ pass
822
+
823
+ if all_alerts:
824
+ for alert in all_alerts:
825
+ if alert['severity'] == 'HIGH':
826
+ st.error(alert['message'])
827
+ elif alert['severity'] == 'MEDIUM':
828
+ st.warning(alert['message'])
829
+ else:
830
+ st.info(alert['message'])
831
+ else:
832
+ st.success(t("all_conditions_normal"))
833
+
834
+ # ════════════════════════════════════════════════════════════════════════════
835
+ # FEATURE 6: MARKET PRICES
836
+ # ════════════════════════════════════════════════════════════════════════════
837
+
838
+ @st.cache_data(ttl=3600)
839
+ def get_live_market_prices():
840
+ """Get live market prices"""
841
+ return {
842
+ t("wheat"): 2250,
843
+ t("rice"): 2650,
844
+ t("cotton"): 5800,
845
+ t("sugarcane"): 295,
846
+ t("potato"): 1650,
847
+ t("tomato"): 1350,
848
+ t("onion"): 1950,
849
+ t("corn"): 2000
850
+ }
851
+
852
+ # ════════════════════════════════════════════════════════════════════════════
853
+ # FEATURE 7: USER AUTHENTICATION
854
+ # ════════════════════════════════════════════════════════════════════════════
855
+
856
+ def init_user_database():
857
+ """Initialize user database"""
858
+ conn = sqlite3.connect('users.db')
859
+ c = conn.cursor()
860
+ c.execute('''CREATE TABLE IF NOT EXISTS users
861
+ (id INTEGER PRIMARY KEY, username TEXT UNIQUE, password TEXT, email TEXT, location TEXT, created_date TEXT)''')
862
+ conn.commit()
863
+ conn.close()
864
+
865
+ def hash_password(password):
866
+ """Hash password"""
867
+ return hashlib.sha256(password.encode()).hexdigest()
868
+
869
+ def register_user(username, password, email, location):
870
+ """Register new user"""
871
+ try:
872
+ conn = sqlite3.connect('users.db')
873
+ c = conn.cursor()
874
+ hashed_pwd = hash_password(password)
875
+ date = datetime.now().strftime("%Y-%m-%d")
876
+ c.execute('INSERT INTO users VALUES (NULL, ?, ?, ?, ?, ?)',
877
+ (username, hashed_pwd, email, location, date))
878
+ conn.commit()
879
+ conn.close()
880
+ return True, "User registered successfully!"
881
+ except sqlite3.IntegrityError:
882
+ return False, "Username already exists"
883
+ except Exception as e:
884
+ return False, str(e)
885
+
886
+ def login_user(username, password):
887
+ """Login user"""
888
+ try:
889
+ conn = sqlite3.connect('users.db')
890
+ c = conn.cursor()
891
+ hashed_pwd = hash_password(password)
892
+ c.execute('SELECT * FROM users WHERE username=? AND password=?', (username, hashed_pwd))
893
+ user = c.fetchone()
894
+ conn.close()
895
+ return (True, user) if user else (False, "Invalid credentials")
896
+ except Exception as e:
897
+ return False, str(e)
898
+
899
+ # ════════════════════════════════════════════════════════════════════════════
900
+ # SESSION STATE INITIALIZATION
901
+ # ════════════════════════════════════════════════════════════════════════════
902
+
903
+ if "location" not in st.session_state:
904
+ st.session_state.location = "महाराष्ट्र"
905
+ if "language" not in st.session_state:
906
+ st.session_state.language = "Hindi"
907
+ if "user_authenticated" not in st.session_state:
908
+ st.session_state.user_authenticated = False
909
+ st.session_state.username = None
910
+ if 'db_initialized' not in st.session_state:
911
+ init_farm_database()
912
+ init_soil_database()
913
+ init_user_database()
914
+ st.session_state.db_initialized = True
915
+
916
+ # ════════════════════════════════════════════════════════════════════════════
917
+ # SIDEBAR - LANGUAGE SELECTION
918
+ # ════════════════════��═══════════════════════════════════════════════════════
919
+
920
+ with st.sidebar:
921
+ st.markdown(f"### {t('settings')}")
922
+
923
+ location = st.selectbox(
924
+ t("location"),
925
+ ["महाराष्ट्र", "पंजाब", "हरियाणा", "उत्तर प्रदेश", "कर्नाटक", "राजस्थान"],
926
+ key="sidebar_location_key"
927
+ )
928
+ st.session_state.location = location
929
+
930
+ language = st.selectbox(
931
+ t("language"),
932
+ ["Hindi", "English", "Marathi"],
933
+ index=["Hindi", "English", "Marathi"].index(st.session_state.language) if st.session_state.language in ["Hindi", "English", "Marathi"] else 0,
934
+ key="sidebar_language_key"
935
+ )
936
+
937
+ if language != st.session_state.language:
938
+ st.session_state.language = language
939
+ st.rerun()
940
+
941
+ st.divider()
942
+ display_alerts()
943
+
944
+ # ════════════════════════════════════════════════════════════════════════════
945
+ # MAIN CONTENT
946
+ # ════════════════════════════════════════════════════════════════════════════
947
+
948
+ st.markdown(f"# {t('title')}")
949
+ st.markdown(f"### {t('subtitle')}")
950
+ st.divider()
951
+
952
+ # DEFINE TABS WITH UNIQUE KEYS
953
+ tab1, tab2, tab3, tab4, tab5, tab6, tab7, tab8, tab9, tab10, tab11, tab12 = st.tabs([
954
+ t("voice_assistant"), t("weather"), t("market"), t("crops"),
955
+ t("pests"), t("irrigation"), t("analytics"), t("disease"),
956
+ t("other"), t("soil"), t("yield"), t("profile")
957
+ ])
958
+
959
+ # ════════════════════════════════════════════════════════════════════════════
960
+ # TAB 1: VOICE INTERACTION
961
+ # ════════════════════════════════════════════════════════════════════════════
962
+
963
+ with tab1:
964
+ st.markdown(t("speak_with_voice"))
965
+ st.markdown(t("what_you_want"))
966
+
967
+ col1, col2, col3 = st.columns(3)
968
+
969
+ with col1:
970
+ st.markdown("<div class='big-emoji'>🌧️</div>", unsafe_allow_html=True)
971
+ if st.button(t("rainfall_info"), key="voice_rainfall_btn", use_container_width=True):
972
+ with st.spinner("..."):
973
+ weather = get_weather_data(st.session_state.location)
974
+ if weather:
975
+ msg = f"Temperature {weather['temperature']:.0f}°C, Humidity {weather['humidity']}%"
976
+ st.success(msg)
977
+ if VOICE_AVAILABLE:
978
+ lang_code = "hi" if st.session_state.language == "Hindi" else "mr" if st.session_state.language == "Marathi" else "en"
979
+ voice_output(msg, language=lang_code)
980
+ else:
981
+ st.info(t("weather_unavailable"))
982
+
983
+ with col2:
984
+ st.markdown("<div class='big-emoji'>🐛</div>", unsafe_allow_html=True)
985
+ if st.button(t("pest_problem"), key="voice_pest_btn", use_container_width=True):
986
+ st.info(t("upload_photo_disease"))
987
+ if VOICE_AVAILABLE:
988
+ lang_code = "hi" if st.session_state.language == "Hindi" else "mr" if st.session_state.language == "Marathi" else "en"
989
+ voice_output(t("go_disease_tab"), language=lang_code)
990
+
991
+ with col3:
992
+ st.markdown("<div class='big-emoji'>💱</div>", unsafe_allow_html=True)
993
+ if st.button(t("market_prices"), key="voice_market_btn", use_container_width=True):
994
+ prices = get_live_market_prices()
995
+ price_list = ", ".join([f"{crop}: ₹{price}" for crop, price in list(prices.items())[:3]])
996
+ st.success(price_list)
997
+ if VOICE_AVAILABLE:
998
+ lang_code = "hi" if st.session_state.language == "Hindi" else "mr" if st.session_state.language == "Marathi" else "en"
999
+ voice_output(price_list, language=lang_code)
1000
+
1001
+ st.divider()
1002
+
1003
+ col1, col2 = st.columns(2)
1004
+
1005
+ with col1:
1006
+ st.markdown(f"<h3 style='text-align: center;'>{t('speak_question')}</h3>", unsafe_allow_html=True)
1007
+ if st.button(t("microphone_on"), key="voice_listen_btn", use_container_width=True):
1008
+ if VOICE_AVAILABLE:
1009
+ lang_code = "hi-IN" if st.session_state.language == "Hindi" else "mr-IN" if st.session_state.language == "Marathi" else "en-IN"
1010
+ text, error = voice_input(language=lang_code)
1011
+ if error:
1012
+ st.error(error)
1013
+ elif text:
1014
+ st.success(f"{t('you_said')} {text}")
1015
+
1016
+ with col2:
1017
+ st.markdown(f"<h3 style='text-align: center;'>{t('hear_answer')}</h3>", unsafe_allow_html=True)
1018
+ if st.button("🔊 Replay", key="voice_replay_btn", use_container_width=True):
1019
+ if VOICE_AVAILABLE:
1020
+ lang_code = "hi" if st.session_state.language == "Hindi" else "mr" if st.session_state.language == "Marathi" else "en"
1021
+ voice_output("Thank you for using Farmer Copilot", language=lang_code)
1022
+
1023
+ # ════════════════════════════════════════════════════════════════════════════
1024
+ # TAB 2: WEATHER
1025
+ # ════════════════════════════════════════════════════════════════════════════
1026
+
1027
+ with tab2:
1028
+ st.markdown(t("weather_info"))
1029
+
1030
+ if st.button(t("refresh_weather"), key="weather_refresh_btn", use_container_width=True):
1031
+ with st.spinner(t("loading")):
1032
+ weather = get_weather_data(st.session_state.location)
1033
+ if weather:
1034
+ col1, col2, col3, col4 = st.columns(4)
1035
+ col1.metric(t("temperature"), f"{weather['temperature']:.1f}°C")
1036
+ col2.metric(t("humidity"), f"{weather['humidity']}%")
1037
+ col3.metric(t("wind"), f"{weather['wind_speed']:.1f} m/s")
1038
+ col4.metric(t("location"), weather['location'])
1039
+ else:
1040
+ st.warning(t("weather_unavailable"))
1041
+
1042
+ # ════════════════════════════════════════════════════════════════════════════
1043
+ # TAB 3: MARKET PRICES
1044
+ # ════════════════════════════════════════════════════════════════════════════
1045
+
1046
+ with tab3:
1047
+ st.markdown(t("market_prices_title"))
1048
+
1049
+ if st.button(t("refresh_prices"), key="market_refresh_btn", use_container_width=True):
1050
+ live_prices = get_live_market_prices()
1051
+ if live_prices:
1052
+ cols = st.columns(2)
1053
+ crops_list = list(live_prices.keys())
1054
+
1055
+ for idx, crop in enumerate(crops_list):
1056
+ col = cols[idx % 2]
1057
+ col.metric(crop, f"₹{live_prices[crop]}{t('per_quintal')}")
1058
+
1059
+ # ════════════════════════════════════════════════════════════════════════════
1060
+ # TAB 4-7: PLACEHOLDER TABS
1061
+ # ════════════════════════════════════════════════════════════════════════════
1062
+
1063
+ with tab4:
1064
+ st.markdown(t("crop_suggestions"))
1065
+ st.info(t("best_crops"))
1066
+ st.write(f"{t('current_season')} {get_current_season()}")
1067
+
1068
+ with tab5:
1069
+ st.markdown(t("pest_management"))
1070
+ st.info(t("pest_info"))
1071
+
1072
+ with tab6:
1073
+ st.markdown(t("irrigation_management"))
1074
+ st.info(t("irrigation_info"))
1075
+
1076
+ with tab7:
1077
+ st.markdown(t("farm_analytics"))
1078
+ st.info(t("analytics_info"))
1079
+
1080
+ # ════════════════════════════════════════════════════════════════════════════
1081
+ # TAB 8: IMAGE RECOGNITION
1082
+ # ════════════════════════════════════════════════════════════════════════════
1083
+
1084
+ with tab8:
1085
+ st.markdown(t("disease_detection"))
1086
+
1087
+ uploaded_file = st.file_uploader(t("upload_leaf"), type=['jpg', 'jpeg', 'png'], key="image_uploader_key")
1088
+
1089
+ if uploaded_file:
1090
+ col1, col2 = st.columns([2, 1])
1091
+
1092
+ with col1:
1093
+ image = Image.open(uploaded_file)
1094
+ st.image(image, caption=t("your_photo"), use_column_width=True)
1095
+
1096
+ with col2:
1097
+ if st.button(t("analyze"), key="image_analyze_btn"):
1098
+ if IMAGE_RECOGNITION_AVAILABLE:
1099
+ result, error = analyze_plant_disease(uploaded_file)
1100
+ if error:
1101
+ st.error(error)
1102
+ else:
1103
+ st.success(t("analysis_complete"))
1104
+ st.metric(t("disease"), result['disease'].split('-')[0])
1105
+ st.metric(t("confidence"), f"{result['confidence']:.1f}%")
1106
+ st.metric(t("severity"), result['severity'])
1107
+ else:
1108
+ st.warning(t("image_recognition_unavailable"))
1109
+
1110
+ # ════════════════════════════════════════════════════════════════════════════
1111
+ # TAB 9: MISC
1112
+ # ════════════════════════════════════════════════════════════════════════════
1113
+
1114
+ with tab9:
1115
+ st.markdown(f"### 🎤 {t('other')}")
1116
+ st.info("Additional voice options")
1117
+
1118
+ # ════════════════════════════════════════════════════════════════════════════
1119
+ # TAB 10: SOIL HEALTH
1120
+ # ════════════════════════════════════════════════════════════════════════════
1121
+
1122
+ with tab10:
1123
+ st.markdown(t("soil_check"))
1124
+
1125
+ col1, col2, col3 = st.columns(3)
1126
+ with col1:
1127
+ pH = st.slider(t("soil_ph"), 4.0, 9.0, 6.5, key="soil_pH_slider_tab10")
1128
+ nitrogen = st.slider(t("nitrogen"), 0, 1000, 250, key="soil_nitrogen_slider_tab10")
1129
+ with col2:
1130
+ phosphorus = st.slider(t("phosphorus"), 0, 100, 20, key="soil_phosphorus_slider_tab10")
1131
+ potassium = st.slider(t("potassium"), 0, 500, 150, key="soil_potassium_slider_tab10")
1132
+ with col3:
1133
+ organic_matter = st.slider(t("organic_matter"), 0.0, 10.0, 2.5, key="soil_organic_slider_tab10")
1134
+ moisture = st.slider(t("moisture"), 0.0, 50.0, 25.0, key="soil_moisture_slider_tab10")
1135
+
1136
+ if st.button(t("save_soil_test"), key="soil_save_btn_tab10", use_container_width=True):
1137
+ save_soil_test(st.session_state.location, pH, nitrogen, phosphorus, potassium, organic_matter, moisture)
1138
+ st.success(t("saved"))
1139
+
1140
+ recommendations = get_soil_recommendations(pH, nitrogen, phosphorus, potassium)
1141
+ st.markdown(t("recommendations"))
1142
+ for rec in recommendations:
1143
+ st.markdown(rec)
1144
+
1145
+ # ════════════════════════════════════════════════════════════════════════════
1146
+ # TAB 11: YIELD PREDICTION
1147
+ # ════════════════════════════════════════════════════════════════════════════
1148
+
1149
+ with tab11:
1150
+ st.markdown(t("yield_prediction"))
1151
+
1152
+ col1, col2, col3 = st.columns(3)
1153
+ with col1:
1154
+ temp = st.slider(t("temp_celsius"), 0, 45, 25, key="yield_temp_slider_tab11")
1155
+ humidity = st.slider(t("humidity_percent"), 0, 100, 70, key="yield_humidity_slider_tab11")
1156
+ with col2:
1157
+ rainfall = st.slider(t("rainfall_mm"), 0, 500, 250, key="yield_rainfall_slider_tab11")
1158
+ pH = st.slider(t("soil_ph"), 4.0, 9.0, 6.8, key="yield_pH_slider_tab11")
1159
+ with col3:
1160
+ org_matter = st.slider(t("organic_matter"), 0.0, 10.0, 2.5, key="yield_orgmatter_slider_tab11")
1161
+
1162
+ if st.button(t("predict_yield"), key="yield_predict_btn_tab11", use_container_width=True):
1163
+ yield_pred = predict_yield(temp, humidity, rainfall, pH, org_matter)
1164
+ st.success(f"{t('estimated_yield')} {yield_pred:.1f} {t('quintal_hectare')}")
1165
+
1166
+ if yield_pred > 25:
1167
+ st.info(t("high_yield"))
1168
+ elif yield_pred > 20:
1169
+ st.info(t("average_yield"))
1170
+ else:
1171
+ st.warning(t("low_yield"))
1172
+
1173
+ # ════════════════════════════════════════════════════════════════════════════
1174
+ # TAB 12: USER PROFILE
1175
+ # ════════════════════════════════════════════════════════════════════════════
1176
+
1177
+ with tab12:
1178
+ st.markdown(t("profile_settings"))
1179
+
1180
+ if not st.session_state.user_authenticated:
1181
+ auth_choice = st.radio(t("choose_option"), [t("login"), t("register")], key="auth_choice_radio_tab12")
1182
+
1183
+ if auth_choice == t("login"):
1184
+ st.subheader(t("login_title"))
1185
+
1186
+ username = st.text_input(t("username"), key="login_username_tab12")
1187
+ password = st.text_input(t("password"), type="password", key="login_password_tab12")
1188
+
1189
+ if st.button(t("login_btn"), key="login_btn_tab12", use_container_width=True):
1190
+ success, result = login_user(username, password)
1191
+
1192
+ if success:
1193
+ st.session_state.user_authenticated = True
1194
+ st.session_state.username = username
1195
+ st.success(t("login_success"))
1196
+ st.rerun()
1197
+ else:
1198
+ st.error(t("invalid_credentials"))
1199
+
1200
+ else:
1201
+ st.subheader(t("register_title"))
1202
+
1203
+ new_username = st.text_input(t("username"), key="register_username_tab12")
1204
+ new_email = st.text_input(t("email"), key="register_email_tab12")
1205
+ new_password = st.text_input(t("password"), type="password", key="register_password_tab12")
1206
+
1207
+ if st.button(t("register_btn"), key="register_btn_tab12", use_container_width=True):
1208
+ success, msg = register_user(new_username, new_password, new_email, st.session_state.location)
1209
+
1210
+ if success:
1211
+ st.success(t("registration_success"))
1212
+ else:
1213
+ st.error(f"❌ {msg}")
1214
+
1215
+ else:
1216
+ st.success(f"{t('logged_in_as')} **{st.session_state.username}**")
1217
+
1218
+ if st.button(t("logout_btn"), key="logout_btn_tab12", use_container_width=True):
1219
+ st.session_state.user_authenticated = False
1220
+ st.session_state.username = None
1221
+ st.rerun()
1222
 
1223
+ st.divider()
1224
+ st.markdown(f"<div style='text-align: center'><p>{t('footer')}</p></div>", unsafe_allow_html=True)