sikeaditya commited on
Commit
e3c97bd
·
verified ·
1 Parent(s): 08dfa32

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +395 -485
app.py CHANGED
@@ -1,524 +1,434 @@
 
1
  import os
2
- from flask import Flask, render_template, request, jsonify
3
  import requests
4
- import pandas as pd
5
- from datetime import datetime
6
- import plotly.express as px
7
- import plotly.io as pio
8
- from googletrans import Translator
9
- import numpy as np
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
  app = Flask(__name__)
 
12
 
13
- # Initialize translator
14
- translator = Translator()
15
-
16
- # Translation dictionaries
17
- MARATHI_TRANSLATIONS = {
18
- 'state': 'राज्य',
19
- 'district': 'जिल्हा',
20
- 'market': 'बाजार',
21
- 'commodity': 'पीक',
22
- 'variety': 'प्रकार',
23
- 'grade': 'श्रेणी',
24
- 'arrival_date': 'आगमन तारीख',
25
- 'min_price': 'किमान किंमत',
26
- 'max_price': 'कमाल किंमत',
27
- 'modal_price': 'सरासरी किंमत',
28
- 'Select State': 'राज्य निवडा',
29
- 'Select District': 'जिल्हा निवडा',
30
- 'Select Market': 'बाजार निवडा',
31
- 'Select Commodity': 'पीक निवडा',
32
- 'Market Data': 'बाजार माहिती',
33
- 'Top 5 Cheapest Crops': 'सर्वात स्वस्त 5 पिके',
34
- 'Top 5 Costliest Crops': 'सर्वात महाग 5 पिके'
35
- }
36
-
37
-
38
- def translate_to_marathi(text):
39
- """Translate text to Marathi"""
40
- try:
41
- if text in MARATHI_TRANSLATIONS:
42
- return MARATHI_TRANSLATIONS[text]
43
- translation = translator.translate(text, dest='mr')
44
- return translation.text
45
- except:
46
- return text
47
 
 
 
48
 
49
- def fetch_market_data(state=None, district=None, market=None, commodity=None):
50
- """Fetch data from the agricultural market API"""
51
- api_key = os.getenv("data_api_key")
52
- print(api_key)
53
- base_url = "1https://api.data.gov.in/resource/9ef84268-d588-465a-a308-a864a43d0070"
54
 
55
- params = {
56
- "api-key": api_key,
57
- "format": "json",
58
- "limit": 15000,
59
- }
60
 
61
- # Add filters if provided
62
- if state:
63
- params["filters[state]"] = state
64
- if district:
65
- params["filters[district]"] = district
66
- if market:
67
- params["filters[market]"] = market
68
- if commodity:
69
- params["filters[commodity]"] = commodity
70
 
71
- try:
72
- response = requests.get(base_url, params=params)
73
- if response.status_code == 200:
74
- data = response.json()
75
- records = data.get("records", [])
76
- df = pd.DataFrame(records)
77
- return df
78
- else:
79
- print(f"API Error: {response.status_code}")
80
- return pd.DataFrame()
81
- except Exception as e:
82
- print(f"Error fetching data: {str(e)}")
83
- return pd.DataFrame()
84
 
 
 
 
85
 
86
- def get_ai_insights(market_data, state, district):
87
- """Get enhanced insights from LLM API with focus on profitable suggestions for farmers"""
88
- if not state or not district or market_data.empty:
89
- return ""
90
 
 
 
 
 
 
 
 
 
 
 
91
  try:
92
- # Calculate additional market metrics
93
- district_data = market_data[market_data['district'] == district]
94
-
95
- # Price trends and volatility
96
- price_trends = district_data.groupby('commodity').agg({
97
- 'modal_price': ['mean', 'min', 'max', 'std']
98
- }).round(2)
99
-
100
- # Calculate price stability (lower std/mean ratio indicates more stable prices)
101
- price_trends['price_stability'] = (price_trends['modal_price']['std'] /
102
- price_trends['modal_price']['mean']).round(2)
103
-
104
- # Identify commodities with consistent high prices
105
- high_value_crops = price_trends[price_trends['modal_price']['mean'] >
106
- price_trends['modal_price']['mean'].median()]
107
-
108
- # Get seasonal patterns
109
- district_data['arrival_date'] = pd.to_datetime(district_data['arrival_date'])
110
- district_data['month'] = district_data['arrival_date'].dt.month
111
- monthly_trends = district_data.groupby(['commodity', 'month'])['modal_price'].mean().round(2)
112
-
113
- # Market competition analysis
114
- market_competition = len(district_data['market'].unique())
115
-
116
- # Prepare comprehensive market summary
117
- market_summary = {
118
- "high_value_crops": high_value_crops.index.tolist(),
119
- "price_stability": price_trends['price_stability'].to_dict(),
120
- "monthly_trends": monthly_trends.to_dict(),
121
- "market_competition": market_competition,
122
- "avg_prices": district_data.groupby('commodity')['modal_price'].mean().round(2).to_dict(),
123
- "price_ranges": {
124
- crop: {
125
- 'min': price_trends.loc[crop, ('modal_price', 'min')],
126
- 'max': price_trends.loc[crop, ('modal_price', 'max')]
127
- } for crop in price_trends.index
128
  }
129
- }
130
-
131
- # Enhanced LLM prompt for more actionable insights
132
- prompt = f"""
133
- As an agricultural market expert, analyze this data for {district}, {state} and provide specific, actionable advice for farmers:
134
-
135
- Market Overview:
136
- - Number of active markets: {market_competition}
137
- - High-value crops: {', '.join(market_summary['high_value_crops'][:5])}
138
- - Price stability data available for {len(market_summary['price_stability'])} crops
139
- - Monthly price trends tracked across {len(market_summary['monthly_trends'])} entries
140
-
141
- Based on this comprehensive data, provide:
142
-
143
- 1. Immediate Market Opportunities (Next 2-4 weeks):
144
- - Which crops currently show the best profit potential?
145
- - Which markets are offering the best prices?
146
- - Any immediate selling or holding recommendations?
147
-
148
- 2. Strategic Planning (Next 3-6 months):
149
- - Which crops show consistent high returns?
150
- - What are the optimal planting times based on price patterns?
151
- - Which crop combinations could maximize profit throughout the year?
152
-
153
- 3. Risk Management:
154
- - Which crops have shown the most stable prices?
155
- - How can farmers diversify their crops to minimize risk?
156
- - What are the warning signs to watch for in the market?
157
-
158
- 4. Market Engagement Strategy:
159
- - Which markets consistently offer better prices?
160
- - What quality grades are fetching premium prices?
161
- - How can farmers negotiate better based on current market dynamics?
162
-
163
- 5. Storage and Timing Recommendations:
164
- - Which crops are worth storing for better prices?
165
- - What are the best times to sell each major crop?
166
- - How can farmers use price trends to time their sales?
167
-
168
- Provide practical, actionable advice that farmers can implement immediately. Include specific numbers and percentages where relevant.
169
- Break the response into clear sections and keep it concise but informative.
170
- """
171
- api_url = "https://api-inference.huggingface.co/models/meta-llama/Llama-3.2-1B-Instruct/v1/chat/completions"
172
- headers = {"Authorization": f"Bearer {os.getenv('HUGGINGFACE_API_KEY')}"}
173
 
174
- payload = {
175
- "inputs": prompt
176
- }
177
 
178
- response = requests.post(api_url, headers=headers, json=payload)
179
- if response.status_code == 200:
180
- response_data = response.json()
181
- if (response_data and
182
- 'choices' in response_data and
183
- len(response_data['choices']) > 0 and
184
- 'message' in response_data['choices'][0] and
185
- 'content' in response_data['choices'][0]['message']):
186
- insights = response_data['choices'][0]['message']['content']
187
- formatted_insights = format_ai_insights(insights)
188
- return formatted_insights
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
 
190
- return "AI insights temporarily unavailable"
191
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  except Exception as e:
193
- print(f"Error generating insights: {str(e)}")
194
- return f"Could not generate insights: {str(e)}"
195
-
196
-
197
- def generate_plots(df, lang='en'):
198
- """Generate all plots with language support"""
199
- if df.empty:
200
- return {}, "No data available"
201
-
202
- # Convert price columns to numeric
203
- price_cols = ['min_price', 'max_price', 'modal_price']
204
- for col in price_cols:
205
- df[col] = pd.to_numeric(df[col], errors='coerce')
206
-
207
- # Color scheme
208
- colors = ["#4CAF50", "#8BC34A", "#CDDC39", "#FFC107", "#FF5722"]
209
-
210
- # 1. Bar Chart
211
- df_bar = df.groupby('commodity')['modal_price'].mean().reset_index()
212
- fig_bar = px.bar(df_bar,
213
- x='commodity',
214
- y='modal_price',
215
- title=translate_to_marathi(
216
- "Average Price by Commodity") if lang == 'mr' else "Average Price by Commodity",
217
- color_discrete_sequence=colors)
218
-
219
- # 2. Line Chart (if commodity selected)
220
- fig_line = None
221
- if 'commodity' in df.columns and len(df['commodity'].unique()) == 1:
222
- df['arrival_date'] = pd.to_datetime(df['arrival_date'])
223
- df_line = df.sort_values('arrival_date')
224
- fig_line = px.line(df_line,
225
- x='arrival_date',
226
- y='modal_price',
227
- title=translate_to_marathi("Price Trend") if lang == 'mr' else "Price Trend",
228
- color_discrete_sequence=colors)
229
-
230
- # 3. Box Plot
231
- fig_box = px.box(df,
232
- x='commodity',
233
- y='modal_price',
234
- title=translate_to_marathi("Price Distribution") if lang == 'mr' else "Price Distribution",
235
- color='commodity',
236
- color_discrete_sequence=colors)
237
-
238
- # Convert to HTML
239
- plots = {
240
- 'bar': pio.to_html(fig_bar, full_html=False),
241
- 'box': pio.to_html(fig_box, full_html=False)
242
- }
243
- if fig_line:
244
- plots['line'] = pio.to_html(fig_line, full_html=False)
245
 
246
- return plots
247
 
 
 
 
 
 
 
 
 
 
248
 
249
- @app.route('/')
250
- def index():
251
- """Render main page"""
252
- initial_data = fetch_market_data()
253
- states = sorted(initial_data['state'].dropna().unique())
254
- return render_template('index.html',
255
- states=states,
256
- today=datetime.today().strftime('%Y-%m-%d'))
257
-
258
-
259
- @app.route('/filter_data', methods=['POST'])
260
- def filter_data():
261
- """Handle data filtering, chart generation, and table generation"""
262
- state = request.form.get('state')
263
- district = request.form.get('district')
264
- market = request.form.get('market')
265
- commodity = request.form.get('commodity')
266
- lang = request.form.get('language', 'en')
267
-
268
- df = fetch_market_data(state, district, market, commodity)
269
- plots = generate_plots(df, lang)
270
- insights = get_ai_insights(df, state, district) if state and district and not df.empty else ""
271
-
272
- # Generate market data table HTML
273
- market_table_html = """
274
- <div class="table-responsive">
275
- <table class="table table-striped table-bordered">
276
- <thead>
277
- <tr>
278
- <th>State</th>
279
- <th>District</th>
280
- <th>Market</th>
281
- <th>Commodity</th>
282
- <th>Variety</th>
283
- <th>Grade</th>
284
- <th>Arrival Date</th>
285
- <th>Min Price</th>
286
- <th>Max Price</th>
287
- <th>Modal Price</th>
288
- </tr>
289
- </thead>
290
- <tbody>
291
- """
292
 
293
- for _, row in df.iterrows():
294
- market_table_html += f"""
295
- <tr>
296
- <td>{row['state']}</td>
297
- <td>{row['district']}</td>
298
- <td>{row['market']}</td>
299
- <td>{row['commodity']}</td>
300
- <td>{row['variety']}</td>
301
- <td>{row['grade']}</td>
302
- <td>{row['arrival_date']}</td>
303
- <td>₹{row['min_price']}</td>
304
- <td>₹{row['max_price']}</td>
305
- <td>₹{row['modal_price']}</td>
306
- </tr>
307
- """
308
- market_table_html += "</tbody></table></div>"
309
-
310
- # Generate top 5 cheapest crops table
311
- cheapest_crops = df.sort_values('modal_price', ascending=True).head(5)
312
- cheapest_table_html = """
313
- <div class="table-responsive">
314
- <table class="table table-sm table-bordered">
315
- <thead>
316
- <tr>
317
- <th>Commodity</th>
318
- <th>Market</th>
319
- <th>Modal Price</th>
320
- </tr>
321
- </thead>
322
- <tbody>
323
- """
324
 
325
- for _, row in cheapest_crops.iterrows():
326
- cheapest_table_html += f"""
327
- <tr>
328
- <td>{row['commodity']}</td>
329
- <td>{row['market']}</td>
330
- <td>₹{row['modal_price']}</td>
331
- </tr>
332
- """
333
- cheapest_table_html += "</tbody></table></div>"
334
-
335
- # Generate top 5 costliest crops table
336
- costliest_crops = df.sort_values('modal_price', ascending=False).head(5)
337
- costliest_table_html = """
338
- <div class="table-responsive">
339
- <table class="table table-sm table-bordered">
340
- <thead>
341
- <tr>
342
- <th>Commodity</th>
343
- <th>Market</th>
344
- <th>Modal Price</th>
345
- </tr>
346
- </thead>
347
- <tbody>
348
- """
349
 
350
- for _, row in costliest_crops.iterrows():
351
- costliest_table_html += f"""
352
- <tr>
353
- <td>{row['commodity']}</td>
354
- <td>{row['market']}</td>
355
- <td>₹{row['modal_price']}</td>
356
- </tr>
357
  """
358
- costliest_table_html += "</tbody></table></div>"
359
-
360
- # Calculate market statistics
361
- market_stats = {
362
- 'total_commodities': len(df['commodity'].unique()),
363
- 'avg_modal_price': f"₹{df['modal_price'].mean():.2f}",
364
- 'price_range': f"₹{df['modal_price'].min():.2f} - ₹{df['modal_price'].max():.2f}",
365
- 'total_markets': len(df['market'].unique())
366
- }
367
 
368
- response = {
369
- 'plots': plots,
370
- 'insights': insights,
371
- 'translations': MARATHI_TRANSLATIONS if lang == 'mr' else {},
372
- 'success': not df.empty,
373
- 'hasStateDistrict': bool(state and district),
374
- 'market_html': market_table_html,
375
- 'cheapest_html': cheapest_table_html,
376
- 'costliest_html': costliest_table_html,
377
- 'market_stats': market_stats
378
- }
379
 
380
- return jsonify(response)
381
-
382
-
383
- def format_ai_insights(insights_data, lang='en'):
384
- """Format AI insights into structured HTML with language support"""
385
- # Translation dictionary for section headers and labels
386
- translations = {
387
- 'AI Market Insights': 'एआय बाजार विश्लेषण',
388
- 'Immediate Market Opportunities': 'तात्काळ बाजार संधी',
389
- 'Best Profit Potential': 'सर्वोत्तम नफा क्षमता',
390
- 'Current Market Status': 'सध्याची बाजार स्थिती',
391
- 'Strategic Planning': 'धोरणात्मक नियोजन',
392
- 'High Return Crops': 'उच्च परतावा पिके',
393
- 'Recommended Crop Combinations': 'शिफारस केलेली पीक संयोजने',
394
- 'Risk Management & Market Strategy': 'जोखीम व्यवस्थापन आणि बाजार धोरण',
395
- 'Recommended Actions': 'शिफारस केलेल्या कृती',
396
- 'increase': 'वाढ',
397
- 'per kg': 'प्रति किलो',
398
- 'Most stable prices': 'सर्वात स्थिर किंमती',
399
- 'Best storage life': 'सर्वोत्तम साठवण कालावधी',
400
- 'Peak selling time': 'उच्चतम विक्री काळ',
401
- 'Plant mix of': 'पिकांचे मिश्रण लावा',
402
- 'Focus on': 'लक्ष केंद्रित करा',
403
- 'Store': 'साठवण करा',
404
- 'Aim for': 'लक्ष्य ठेवा',
405
- 'months': 'महिने'
406
- }
407
 
408
- def translate_text(text):
409
- """Translate text based on language selection"""
410
- if lang == 'mr':
411
- # Try to find direct translation from dictionary
412
- for eng, mar in translations.items():
413
- text = text.replace(eng, mar)
414
- return text
415
- return text
416
-
417
- def format_price(price_text):
418
- """Format price with proper currency symbol and translation"""
419
- if lang == 'mr':
420
- return price_text.replace('₹', '₹').replace('per kg', 'प्रति किलो')
421
- return price_text
422
-
423
- """Format AI insights into structured HTML"""
424
- html = f"""
425
- <div class="insights-header">
426
- <h3 class="en">AI Market Insights</h3>
427
- <h3 class="mr" style="display:none;">एआय बाजार विश्लेषण</h3>
428
- </div>
429
-
430
- <div class="insight-section">
431
- <h4>Immediate Market Opportunities</h4>
432
- <div class="insight-card">
433
- <h5>Best Profit Potential</h5>
434
- <ul class="insight-list">
435
- <li>Beetroot and Bitter gourd showing <span class="percentage-up">15% increase</span> from base year</li>
436
- <li>Bottle gourd premium quality fetching <span class="price-highlight">₹150 per kg</span></li>
437
- </ul>
438
- </div>
439
-
440
- <div class="insight-card">
441
- <h5>Current Market Status</h5>
442
- <ul class="insight-list">
443
- <li>Brinjal in high demand with stable price of <span class="price-highlight">₹80 per kg</span></li>
444
- <li>Premium quality bottle gourd commanding <span class="price-highlight">₹200 per kg</span></li>
445
- </ul>
446
- </div>
447
- </div>
448
-
449
- <div class="insight-section">
450
- <h4>Strategic Planning</h4>
451
- <div class="insight-card">
452
- <h5>High Return Crops</h5>
453
- <ul class="insight-list">
454
- <li>Cauliflower showing <span class="percentage-up">20% increase</span> from base year</li>
455
- <li>Best planting time: Spring season for cauliflower and bottle gourd</li>
456
- </ul>
457
- </div>
458
-
459
- <div class="insight-card">
460
- <h5>Recommended Crop Combinations</h5>
461
- <ul class="insight-list">
462
- <li>Brinjal + Bottle gourd + Cauliflower (similar demand patterns)</li>
463
- </ul>
464
- </div>
465
- </div>
466
-
467
- <div class="insight-section">
468
- <h4>Risk Management & Market Strategy</h4>
469
- <div class="insight-card">
470
- <ul class="insight-list">
471
- <li>Most stable prices: Brinjal, Bottle gourd, Cauliflower</li>
472
- <li>Best storage life: 6-9 months for Cauliflower, Brinjal, and Bottle gourd</li>
473
- <li>Peak selling time for Cauliflower: March-April</li>
474
- </ul>
475
- </div>
476
- </div>
477
-
478
- <div class="action-box">
479
- <h5>Recommended Actions</h5>
480
- <ul class="action-list">
481
- <li>Plant mix of beetroot, bitter gourd, bottle gourd, brinjal, and cauliflower</li>
482
- <li>Focus on stable price markets for cauliflower and bottle gourd</li>
483
- <li>Store cauliflower for March-April peak prices</li>
484
- <li>Aim for premium quality grades to maximize profits</li>
485
- </ul>
486
- </div>
487
- """
488
- if lang == 'mr':
489
- html = translate_text(html)
490
- # print(html
491
- return html
492
 
493
- return html
 
 
 
 
 
 
 
 
 
 
 
494
 
 
 
 
 
495
 
496
- @app.route('/get_districts', methods=['POST'])
497
- def get_districts():
498
- """Get districts for selected state"""
499
- state = request.form.get('state')
500
- df = fetch_market_data(state=state)
501
- districts = sorted(df['district'].dropna().unique())
502
- return jsonify(districts)
503
 
 
 
 
 
 
504
 
505
- @app.route('/get_markets', methods=['POST'])
506
- def get_markets():
507
- """Get markets for selected district"""
508
- district = request.form.get('district')
509
- df = fetch_market_data(district=district)
510
- markets = sorted(df['market'].dropna().unique())
511
- return jsonify(markets)
512
 
 
513
 
514
- @app.route('/get_commodities', methods=['POST'])
515
- def get_commodities():
516
- """Get commodities for selected market"""
517
- market = request.form.get('market')
518
- df = fetch_market_data(market=market)
519
- commodities = sorted(df['commodity'].dropna().unique())
520
- return jsonify(commodities)
521
 
522
 
523
- if __name__== '__main__':
524
- app.run(host='0.0.0.0', port=7860)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
  import os
3
+ from flask import Flask, render_template, request, jsonify, redirect, url_for, flash, session
4
  import requests
5
+ from werkzeug.utils import secure_filename
6
+ import google.generativeai as genai
7
+ import base64
8
+ import json
9
+ from datetime import datetime, timedelta
10
+ import threading
11
+ import time
12
+ from gtts import gTTS # <-- New import for audio generation
13
+
14
+
15
+
16
+ # Configure the Gemini API
17
+ GEMINI_API_KEY = "AIzaSyBtXV2xJbrWVV57B5RWy_meKXOA59HFMeY"
18
+ if not GEMINI_API_KEY:
19
+ raise ValueError("Google API Key not found. Set it as GEMINI_API_KEY in the Space settings.")
20
+
21
+ genai.configure(api_key=GEMINI_API_KEY)
22
+
23
+ # Setup the Gemini model
24
+ model = genai.GenerativeModel('gemini-1.5-flash')
25
 
26
  app = Flask(__name__)
27
+ app.secret_key = os.getenv("SECRET_KEY", "your-default-secret-key-for-flash-messages")
28
 
29
+ # Configure upload folder
30
+ UPLOAD_FOLDER = 'static/uploads'
31
+ ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
+ if not os.path.exists(UPLOAD_FOLDER):
34
+ os.makedirs(UPLOAD_FOLDER)
35
 
36
+ # Configure audio folder (new)
37
+ AUDIO_FOLDER = 'static/audio'
38
+ if not os.path.exists(AUDIO_FOLDER):
39
+ os.makedirs(AUDIO_FOLDER)
 
40
 
41
+ app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
 
 
 
 
42
 
 
 
 
 
 
 
 
 
 
43
 
44
+ def allowed_file(filename):
45
+ return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
46
+
 
 
 
 
 
 
 
 
 
 
47
 
48
+ def encode_image(image_path):
49
+ with open(image_path, "rb") as image_file:
50
+ return base64.b64encode(image_file.read()).decode('utf-8')
51
 
 
 
 
 
52
 
53
+ def get_web_pesticide_info(disease, plant_type="Unknown"):
54
+ """Fetch pesticide information from web sources for a specific disease and plant type"""
55
+ query = f"site:agrowon.esakal.com {disease} in {plant_type}"
56
+ url = "https://www.googleapis.com/customsearch/v1"
57
+ params = {
58
+ "key": os.getenv("GOOGLE_API_KEY"),
59
+ "cx": os.getenv("GOOGLE_CX"),
60
+ "q": query,
61
+ "num": 3
62
+ }
63
  try:
64
+ response = requests.get(url, params=params)
65
+ response.raise_for_status()
66
+ data = response.json()
67
+ if "items" in data and len(data["items"]) > 0:
68
+ item = data["items"][0]
69
+ return {
70
+ "title": item.get("title", "No title available"),
71
+ "link": item.get("link", "#"),
72
+ "snippet": item.get("snippet", "No snippet available"),
73
+ "summary": item.get("snippet", "No snippet available")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  }
75
+ except Exception as e:
76
+ print(f"Error retrieving web pesticide info: {str(e)}")
77
+ return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
 
 
 
 
79
 
80
+ def get_more_web_info(query):
81
+ """Get more general web information based on a search query"""
82
+ url = "https://www.googleapis.com/customsearch/v1"
83
+ params = {
84
+ "key": os.getenv("GOOGLE_API_KEY"),
85
+ "cx": os.getenv("GOOGLE_CX"),
86
+ "q": query,
87
+ "num": 3
88
+ }
89
+ try:
90
+ response = requests.get(url, params=params)
91
+ response.raise_for_status()
92
+ data = response.json()
93
+ results = []
94
+ if "items" in data:
95
+ for item in data["items"]:
96
+ results.append({
97
+ "title": item.get("title", "No title available"),
98
+ "link": item.get("link", "#"),
99
+ "snippet": item.get("snippet", "No snippet available")
100
+ })
101
+ return results
102
+ except Exception as e:
103
+ print(f"Error retrieving additional articles: {str(e)}")
104
+ return []
105
 
 
106
 
107
+ def get_commercial_product_info(recommendation, disease_name):
108
+ """Fetch commercial product information related to a pesticide recommendation.
109
+ If no relevant products are found from web sources, return default products based on issue type:
110
+ bacterial, fungicide (disease), or insecticide.
111
+ """
112
+ indiamart_query = f"site:indiamart.com pesticide '{disease_name}' '{recommendation}'"
113
+ krishi_query = f"site:krishisevakendra.in/products pesticide '{disease_name}' '{recommendation}'"
114
+
115
+ indiamart_results = get_more_web_info(indiamart_query)
116
+ krishi_results = get_more_web_info(krishi_query)
117
+
118
+ results = indiamart_results + krishi_results
119
+
120
+ if not results:
121
+ lower_disease = disease_name.lower()
122
+ lower_recommendation = recommendation.lower()
123
+
124
+ if ("bacteria" in lower_disease or "bacterial" in lower_disease or
125
+ "bacteria" in lower_recommendation or "bacterial" in lower_recommendation):
126
+ results = [
127
+ {
128
+ "title": "UPL SAAF Carbendazin Mancozeb Bactericide",
129
+ "link": "https://www.amazon.in/UPL-SAAF-Carbendazinm12-Mancozeb63-Action/dp/B0DJLQRL44",
130
+ "snippet": "Bactericide for controlling bacterial infections."
131
+ },
132
+ {
133
+ "title": "Tropical Tagmycin Bactericide",
134
+ "link": "https://krushidukan.bharatagri.com/en/products/tropical-tagmycin-bactericide",
135
+ "snippet": "Bactericide for effective bacterial infection management."
136
+ }
137
+ ]
138
+ elif ("fungus" in lower_disease or "fungicide" in lower_recommendation or
139
+ "antibiotic" in lower_recommendation or "disease" in lower_disease):
140
+ results = [
141
+ {
142
+ "title": "Plantomycin Bio Organic Antibiotic Effective Disease",
143
+ "link": "https://www.amazon.in/Plantomycin-Bio-Organic-Antibiotic-Effective-Disease/dp/B0DRVVJKQ4",
144
+ "snippet": "Bio organic antibiotic for effective control of plant diseases."
145
+ },
146
+ {
147
+ "title": "WET-TREE Larvicide Thuringiensis Insecticide",
148
+ "link": "https://www.amazon.in/WET-TREE-Larvicide-Thuringiensis-Insecticide/dp/B0D6R72KHV",
149
+ "snippet": "Larvicide with thuringiensis for disease prevention."
150
+ }
151
+ ]
152
+ elif ("insecticide" in lower_disease or "insect" in lower_disease or "pest" in lower_disease or
153
+ "insecticide" in lower_recommendation or "insect" in lower_recommendation or "pest" in lower_recommendation):
154
+ results = [
155
+ {
156
+ "title": "Syngenta Actara Insecticide",
157
+ "link": "https://www.amazon.in/syngenta-Actara-Insect-Repellent-Insecticide/dp/B08W55XTHS",
158
+ "snippet": "Effective systemic insecticide for pest control."
159
+ },
160
+ {
161
+ "title": "Cyhalothrin Insecticide",
162
+ "link": "https://www.amazon.in/Cyhalothrin-Control-Eradication-Mosquitoes-Crawling/dp/B01N53VH1T",
163
+ "snippet": "Broad-spectrum insecticide for pest management."
164
+ }
165
+ ]
166
+ else:
167
+ results = [
168
+ {
169
+ "title": "Syngenta Actara Insecticide",
170
+ "link": "https://www.amazon.in/syngenta-Actara-Insect-Repellent-Insecticide/dp/B08W55XTHS",
171
+ "snippet": "Effective systemic insecticide for pest control."
172
+ },
173
+ {
174
+ "title": "Cyhalothrin Insecticide",
175
+ "link": "https://www.amazon.in/Cyhalothrin-Control-Eradication-Mosquitoes-Crawling/dp/B01N53VH1T",
176
+ "snippet": "Broad-spectrum insecticide for pest management."
177
+ }
178
+ ]
179
+
180
+ return results
181
+
182
+
183
+ def get_relevant_feedback(plant_name):
184
+ """Retrieve feedback entries relevant to the given plant name from feedback.json."""
185
+ feedback_file = "feedback.json"
186
+ if os.path.exists(feedback_file):
187
+ try:
188
+ with open(feedback_file, "r") as f:
189
+ all_feedback = json.load(f)
190
+ relevant = [entry.get("feedback") for entry in all_feedback if
191
+ entry.get("plant_name", "").lower() == plant_name.lower()]
192
+ if relevant:
193
+ return " ".join(relevant[:3])
194
+ except Exception as e:
195
+ print(f"Error reading feedback for reinforcement: {e}")
196
+ return ""
197
+
198
+
199
+ def generate_audio(text, language, filename):
200
+ """Generate an MP3 file from text using gTTS."""
201
+ try:
202
+ tts = gTTS(text=text, lang=language, slow=False)
203
+ tts.save(filename)
204
  except Exception as e:
205
+ print(f"Error generating audio: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
 
 
207
 
208
+ def analyze_plant_image(image_path, plant_name, language):
209
+ try:
210
+ # Load the image
211
+ image_parts = [
212
+ {
213
+ "mime_type": "image/jpeg",
214
+ "data": encode_image(image_path)
215
+ }
216
+ ]
217
 
218
+ # Load relevant feedback (reinforcement data) for this plant
219
+ feedback_context = get_relevant_feedback(plant_name)
220
+ feedback_instruction = f" Please consider the following user feedback from similar cases: {feedback_context}" if feedback_context else ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
 
222
+ # Create prompt for Gemini API with language instruction and feedback reinforcement if available
223
+ prompt = f"""
224
+ Analyze this image of a {plant_name} plant and prioritize determining if it's healthy or has a disease or pest infestation.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
 
226
+ If a disease or pest is detected, provide the following information in JSON format:
227
+ {{"results": [{{"type": "disease/pest", "name": "Name of disease or pest", "probability": "Probability as a percentage", "symptoms": "Describe the visible symptoms", "causes": "Main causes of the disease or pest", "severity": "Low/Medium/High", "spreading": "How it spreads", "treatment": "Treatment options", "prevention": "Preventive measures"}},{{}},{{}}], "is_healthy": boolean indicating if the plant appears healthy, "confidence": "Overall confidence in the analysis as a percentage"}}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
228
 
229
+ Only return the JSON data and nothing else. Ensure the JSON is valid and properly formatted.
230
+ If the plant appears completely healthy, set is_healthy to true and include an empty results array.
231
+ Additionally, provide the response in {language} language.
232
+ and at end show which all data from feedback was taken into consideration and if no data was taken so no data.
 
 
 
233
  """
 
 
 
 
 
 
 
 
 
234
 
235
+ # Send request to Gemini API
236
+ response = model.generate_content([prompt] + image_parts)
 
 
 
 
 
 
 
 
 
237
 
238
+ # Extract the JSON response
239
+ response_text = response.text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
 
241
+ # Find JSON within response text if needed
242
+ json_start = response_text.find('{')
243
+ json_end = response_text.rfind('}') + 1
244
+
245
+ if json_start >= 0 and json_end > 0:
246
+ json_str = response_text[json_start:json_end]
247
+ analysis_result = json.loads(json_str)
248
+ else:
249
+ return {
250
+ "error": "Failed to parse the API response",
251
+ "raw_response": response_text
252
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
 
254
+ # ---- Added audio generation feature ----
255
+ # Create a summary text based on the analysis including Spreading, Treatment, and Prevention
256
+ if analysis_result.get('is_healthy', False):
257
+ summary_text = f"Your {plant_name} plant appears to be healthy. Continue with your current care practices."
258
+ elif 'results' in analysis_result and analysis_result['results']:
259
+ summary_text = "Detected issues: "
260
+ for result in analysis_result['results']:
261
+ summary_text += (f"{result.get('name', 'Unknown')}. Symptoms: {result.get('symptoms', '')}. "
262
+ f"Causes: {result.get('causes', '')}. Spreading: {result.get('spreading', '')}. "
263
+ f"Treatment: {result.get('treatment', '')}. Prevention: {result.get('prevention', '')}. ")
264
+ else:
265
+ summary_text = "Analysis inconclusive."
266
 
267
+ # Map language name to gTTS language code
268
+ lang_mapping = {"English": "en", "Hindi": "hi", "Bengali": "bn", "Telugu": "te", "Marathi": "mr", "Tamil": "ta",
269
+ "Gujarati": "gu", "Urdu": "ur", "Kannada": "kn", "Odia": "or", "Malayalam": "ml"}
270
+ gtts_lang = lang_mapping.get(language, 'en')
271
 
272
+ # Generate unique audio filename
273
+ timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
274
+ audio_filename = f"audio_result.mp3"
275
+ audio_path = os.path.join(AUDIO_FOLDER, audio_filename)
276
+ generate_audio(summary_text, gtts_lang, audio_path)
 
 
277
 
278
+ # Wait until the audio file is created and has nonzero size (up to 5 seconds)
279
+ wait_time = 0
280
+ while (not os.path.exists(audio_path) or os.path.getsize(audio_path) == 0) and wait_time < 5:
281
+ time.sleep(0.5)
282
+ wait_time += 0.5
283
 
284
+ # Add relative audio file path for template rendering
285
+ analysis_result['audio_file'] = os.path.join('audio', audio_filename)
286
+ # -----------------------------------------
 
 
 
 
287
 
288
+ return analysis_result
289
 
290
+ except Exception as e:
291
+ return {
292
+ "error": str(e),
293
+ "is_healthy": None,
294
+ "results": []
295
+ }
 
296
 
297
 
298
+ def cleanup_old_files(directory, max_age_hours=1): # Reduced to 1 hour for Hugging Face
299
+ """Remove files older than the specified age from the directory"""
300
+ while True:
301
+ now = datetime.now()
302
+ for filename in os.listdir(directory):
303
+ if filename == '.gitkeep': # Skip the .gitkeep file
304
+ continue
305
+ file_path = os.path.join(directory, filename)
306
+ file_age = now - datetime.fromtimestamp(os.path.getctime(file_path))
307
+ if file_age > timedelta(hours=max_age_hours):
308
+ try:
309
+ os.remove(file_path)
310
+ print(f"Removed old file: {file_path}")
311
+ except Exception as e:
312
+ print(f"Error removing {file_path}: {e}")
313
+ time.sleep(300) # 5 minutes
314
+
315
+
316
+ @app.route('/', methods=['GET'])
317
+ def index():
318
+ # GET request - show the upload form
319
+ return render_template('index.html', show_results=False)
320
+
321
+
322
+ @app.route('/feedback', methods=['POST'])
323
+ def feedback():
324
+ # Get feedback from form submission
325
+ feedback_text = request.form.get("feedback")
326
+ plant_name = request.form.get("plant_name", "Unknown")
327
+ if not feedback_text:
328
+ flash("Please provide your feedback before submitting.")
329
+ return redirect(url_for('index'))
330
+ feedback_data = {
331
+ "plant_name": plant_name,
332
+ "feedback": feedback_text,
333
+ "timestamp": datetime.now().isoformat()
334
+ }
335
+ feedback_file = "feedback.json"
336
+ if os.path.exists(feedback_file):
337
+ try:
338
+ with open(feedback_file, "r") as f:
339
+ existing_feedback = json.load(f)
340
+ except Exception as e:
341
+ print(f"Error reading feedback file: {e}")
342
+ existing_feedback = []
343
+ else:
344
+ existing_feedback = []
345
+ existing_feedback.append(feedback_data)
346
+ try:
347
+ with open(feedback_file, "w") as f:
348
+ json.dump(existing_feedback, f, indent=4)
349
+ except Exception as e:
350
+ flash(f"Error saving your feedback: {str(e)}")
351
+ return redirect(url_for('index'))
352
+ flash("Thank you for your feedback!")
353
+ return redirect(url_for('index'))
354
+
355
+
356
+ @app.route('/analyze', methods=['POST'])
357
+ def analyze():
358
+ if 'plant_image' not in request.files:
359
+ flash('No file part')
360
+ return redirect(url_for('index'))
361
+
362
+ file = request.files['plant_image']
363
+ plant_name = request.form.get('plant_name', 'unknown')
364
+ language = request.form.get('language', 'English')
365
+
366
+ if file.filename == '':
367
+ flash('No selected file')
368
+ return redirect(url_for('index'))
369
+
370
+ if file and allowed_file(file.filename):
371
+ timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
372
+ original_filename = secure_filename(file.filename)
373
+ filename = f"{timestamp}_{original_filename}"
374
+ file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
375
+ file.save(file_path)
376
+
377
+ try:
378
+ analysis_result = analyze_plant_image(file_path, plant_name, language)
379
+ if 'error' in analysis_result:
380
+ flash(f"Error analyzing image: {analysis_result['error']}")
381
+ if os.path.exists(file_path):
382
+ os.remove(file_path)
383
+ return redirect(url_for('index'))
384
+ web_info = {}
385
+ product_info = {}
386
+ if not analysis_result.get('is_healthy', False) and 'results' in analysis_result:
387
+ for result in analysis_result['results']:
388
+ disease_name = result.get('name', '')
389
+ if disease_name:
390
+ web_info[disease_name] = get_web_pesticide_info(disease_name, plant_name)
391
+ treatment = result.get('treatment', '')
392
+ if treatment:
393
+ product_info[disease_name] = get_commercial_product_info(treatment, disease_name)
394
+ response = render_template(
395
+ 'results.html',
396
+ results=analysis_result,
397
+ plant_name=plant_name,
398
+ image_path=file_path.replace('static/', '', 1),
399
+ web_info=web_info,
400
+ product_info=product_info
401
+ )
402
+
403
+ def delete_file_after_delay(path, delay=30):
404
+ time.sleep(delay)
405
+ if os.path.exists(path):
406
+ try:
407
+ os.remove(path)
408
+ print(f"Deleted analyzed file: {path}")
409
+ except Exception as e:
410
+ print(f"Error deleting {path}: {e}")
411
+
412
+ threading.Thread(
413
+ target=delete_file_after_delay,
414
+ args=(file_path,),
415
+ daemon=True
416
+ ).start()
417
+
418
+ return response
419
+
420
+ except Exception as e:
421
+ flash(f"An error occurred: {str(e)}")
422
+ if os.path.exists(file_path):
423
+ os.remove(file_path)
424
+ return redirect(url_for('index'))
425
+
426
+ flash('Invalid file type. Please upload an image (png, jpg, jpeg, gif).')
427
+ return redirect(url_for('index'))
428
+
429
+
430
+ if __name__ == '__main__':
431
+ cleanup_thread = threading.Thread(target=cleanup_old_files, args=(app.config['UPLOAD_FOLDER'],), daemon=True)
432
+ cleanup_thread.start()
433
+ port = int(os.environ.get("PORT", 7860))
434
+ app.run(host='0.0.0.0', port=port)