sikeaditya commited on
Commit
79ffdb8
·
verified ·
1 Parent(s): 6159b71

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +433 -436
app.py CHANGED
@@ -1,437 +1,434 @@
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
- from dotenv import load_dotenv
8
- import base64
9
- import json
10
- from datetime import datetime, timedelta
11
- import threading
12
- import time
13
-
14
- # Load environment variables
15
- load_dotenv()
16
-
17
- # Configure the Gemini API
18
- # Try to get API key from environment variable, first from HF_SPACES then from .env file
19
- # GEMINI_API_KEY = os.getenv("HF_GEMINI_API_KEY") or os.getenv("GEMINI_API_KEY")
20
- GEMINI_API_KEY = os.getenv("HF_GEMINI_API_KEY") or os.getenv("GEMINI_API_KEY")
21
- if not GEMINI_API_KEY:
22
- raise ValueError("Google API Key not found. Set it as GEMINI_API_KEY in the Space settings.")
23
-
24
- genai.configure(api_key=GEMINI_API_KEY)
25
-
26
- # Setup the Gemini model
27
- model = genai.GenerativeModel('gemini-1.5-flash')
28
-
29
- app = Flask(__name__)
30
- app.secret_key = os.getenv("SECRET_KEY", "your-default-secret-key-for-flash-messages")
31
-
32
- # Configure upload folder
33
- UPLOAD_FOLDER = 'static/uploads'
34
- ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
35
-
36
- if not os.path.exists(UPLOAD_FOLDER):
37
- os.makedirs(UPLOAD_FOLDER)
38
-
39
- app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
40
-
41
- def allowed_file(filename):
42
- return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
43
-
44
- def encode_image(image_path):
45
- with open(image_path, "rb") as image_file:
46
- return base64.b64encode(image_file.read()).decode('utf-8')
47
-
48
- def get_web_pesticide_info(disease, plant_type="Unknown"):
49
- """Fetch pesticide information from web sources for a specific disease and plant type"""
50
- query = f"site:agrowon.esakal.com {disease} in {plant_type}"
51
- url = "https://www.googleapis.com/customsearch/v1"
52
- params = {
53
- "key": os.getenv("GOOGLE_API_KEY"),
54
- "cx": os.getenv("GOOGLE_CX"),
55
- "q": query,
56
- "num": 3
57
- }
58
- try:
59
- response = requests.get(url, params=params)
60
- response.raise_for_status()
61
- data = response.json()
62
- if "items" in data and len(data["items"]) > 0:
63
- item = data["items"][0]
64
- return {
65
- "title": item.get("title", "No title available"),
66
- "link": item.get("link", "#"),
67
- "snippet": item.get("snippet", "No snippet available"),
68
- "summary": item.get("snippet", "No snippet available")
69
- }
70
- except Exception as e:
71
- print(f"Error retrieving web pesticide info: {str(e)}")
72
- return None
73
-
74
- def get_more_web_info(query):
75
- """Get more general web information based on a search query"""
76
- url = "https://www.googleapis.com/customsearch/v1"
77
- params = {
78
- "key": os.getenv("GOOGLE_API_KEY"),
79
- "cx": os.getenv("GOOGLE_CX"),
80
- "q": query,
81
- "num": 3
82
- }
83
- try:
84
- response = requests.get(url, params=params)
85
- response.raise_for_status()
86
- data = response.json()
87
- results = []
88
- if "items" in data:
89
- for item in data["items"]:
90
- results.append({
91
- "title": item.get("title", "No title available"),
92
- "link": item.get("link", "#"),
93
- "snippet": item.get("snippet", "No snippet available")
94
- })
95
- return results
96
- except Exception as e:
97
- print(f"Error retrieving additional articles: {str(e)}")
98
- return []
99
-
100
- def get_commercial_product_info(recommendation, disease_name):
101
- """Fetch commercial product information related to a pesticide recommendation.
102
- If no relevant products are found from web sources, return default products based on issue type:
103
- bacterial, fungicide (disease), or insecticide.
104
- """
105
- indiamart_query = f"site:indiamart.com pesticide '{disease_name}' '{recommendation}'"
106
- krishi_query = f"site:krishisevakendra.in/products pesticide '{disease_name}' '{recommendation}'"
107
-
108
- indiamart_results = get_more_web_info(indiamart_query)
109
- krishi_results = get_more_web_info(krishi_query)
110
-
111
- # Merge results from both sources
112
- results = indiamart_results + krishi_results
113
-
114
- if not results:
115
- lower_disease = disease_name.lower()
116
- lower_recommendation = recommendation.lower()
117
-
118
- # Bacterial fallback
119
- if ("bacteria" in lower_disease or "bacterial" in lower_disease or
120
- "bacteria" in lower_recommendation or "bacterial" in lower_recommendation):
121
- results = [
122
- {
123
- "title": "UPL SAAF Carbendazin Mancozeb Bactericide",
124
- "link": "https://www.amazon.in/UPL-SAAF-Carbendazinm12-Mancozeb63-Action/dp/B0DJLQRL44?source=ps-sl-shoppingads-lpcontext&ref_=fplfs&psc=1&smid=A1BIAFERY87G8Q",
125
- "snippet": "Bactericide for controlling bacterial infections."
126
- },
127
- {
128
- "title": "Tropical Tagmycin Bactericide",
129
- "link": "https://krushidukan.bharatagri.com/en/products/tropical-tagmycin-bactericide?variant=46519705895155&country=IN&currency=INR&utm_medium=product_sync&utm_source=google&utm_content=sag_organic&utm_campaign=sag_organic&srsltid=AfmBOoptFf8O3lpleZBgvI7pIOYUnHP6EWoZ-M6vGZ2er8VYU2PzVbkc7sc",
130
- "snippet": "Bactericide for effective bacterial infection management."
131
- }
132
- ]
133
- # Fungicide / Disease fallback
134
- elif ("fungus" in lower_disease or "fungicide" in lower_recommendation or
135
- "antibiotic" in lower_recommendation or "disease" in lower_disease):
136
- results = [
137
- {
138
- "title": "Plantomycin Bio Organic Antibiotic Effective Disease",
139
- "link": "https://www.amazon.in/Plantomycin-Bio-Organic-Antibiotic-Effective-Disease/dp/B0DRVVJKQ4?source=ps-sl-shoppingads-lpcontext&ref_=fplfs&psc=1&smid=A2PDMX630A5EG6",
140
- "snippet": "Bio organic antibiotic for effective control of plant diseases."
141
- },
142
- {
143
- "title": "WET-TREE Larvicide Thuringiensis Insecticide",
144
- "link": "https://www.amazon.in/WET-TREE-Larvicide-Thuringiensis-Insecticide/dp/B0D6R72KHV?source=ps-sl-shoppingads-lpcontext&ref_=fplfs&psc=1&smid=A3V4YZ24A56I42",
145
- "snippet": "Larvicide with thuringiensis for disease prevention."
146
- },
147
- {
148
- "title": "WET-TREE Larvicide Thuringiensis Insecticide",
149
- "link": "https://www.amazon.in/WET-TREE-Larvicide-Thuringiensis-Insecticide/dp/B0D6R72KHV?source=ps-sl-shoppingads-lpcontext&ref_=fplfs&psc=1&smid=A3V4YZ24A56I42",
150
- "snippet": "Larvicide with thuringiensis for disease prevention."
151
- }
152
- ]
153
- # Insecticide fallback
154
- elif ("insecticide" in lower_disease or "insect" in lower_disease or "pest" in lower_disease or
155
- "insecticide" in lower_recommendation or "insect" in lower_recommendation or "pest" in lower_recommendation):
156
- results = [
157
- {
158
- "title": "Syngenta Actara Insecticide",
159
- "link": "https://www.amazon.in/syngenta-Actara-Insect-Repellent-Insecticide/dp/B08W55XTHS?source=ps-sl-shoppingads-lpcontext&ref_=fplfs&psc=1&smid=A3ABQWNNCUI42M",
160
- "snippet": "Effective systemic insecticide for pest control."
161
- },
162
- {
163
- "title": "Cyhalothrin Insecticide",
164
- "link": "https://www.amazon.in/Cyhalothrin-Control-Eradication-Mosquitoes-Crawling/dp/B01N53VH1T?source=ps-sl-shoppingads-lpcontext&ref_=fplfs&psc=1&smid=A1ZSKCLHK592D5",
165
- "snippet": "Broad-spectrum insecticide for pest management."
166
- }
167
- ]
168
- # Default fallback to insecticide if none of the above match
169
- else:
170
- results = [
171
- {
172
- "title": "Syngenta Actara Insecticide",
173
- "link": "https://www.amazon.in/syngenta-Actara-Insect-Repellent-Insecticide/dp/B08W55XTHS?source=ps-sl-shoppingads-lpcontext&ref_=fplfs&psc=1&smid=A3ABQWNNCUI42M",
174
- "snippet": "Effective systemic insecticide for pest control."
175
- },
176
- {
177
- "title": "Cyhalothrin Insecticide",
178
- "link": "https://www.amazon.in/Cyhalothrin-Control-Eradication-Mosquitoes-Crawling/dp/B01N53VH1T?source=ps-sl-shoppingads-lpcontext&ref_=fplfs&psc=1&smid=A1ZSKCLHK592D5",
179
- "snippet": "Broad-spectrum insecticide for pest management."
180
- }
181
- ]
182
-
183
- return results
184
-
185
-
186
- def get_relevant_feedback(plant_name):
187
- """Retrieve feedback entries relevant to the given plant name from feedback.json."""
188
- feedback_file = "feedback.json"
189
- if os.path.exists(feedback_file):
190
- try:
191
- with open(feedback_file, "r") as f:
192
- all_feedback = json.load(f)
193
- # Filter feedback where the plant_name matches (case-insensitive)
194
- relevant = [entry.get("feedback") for entry in all_feedback if entry.get("plant_name", "").lower() == plant_name.lower()]
195
- # Optionally limit to a few items (e.g., first 3)
196
- if relevant:
197
- return " ".join(relevant[:3])
198
- except Exception as e:
199
- print(f"Error reading feedback for reinforcement: {e}")
200
- return ""
201
-
202
-
203
- def analyze_plant_image(image_path, plant_name, language):
204
- try:
205
- # Load the image
206
- image_parts = [
207
- {
208
- "mime_type": "image/jpeg",
209
- "data": encode_image(image_path)
210
- }
211
- ]
212
-
213
- # Load relevant feedback (reinforcement data) for this plant
214
- feedback_context = get_relevant_feedback(plant_name)
215
- feedback_instruction = f" Please consider the following user feedback from similar cases: {feedback_context}" if feedback_context else ""
216
-
217
- # Create prompt for Gemini API with language instruction and feedback reinforcement if available
218
- prompt = f"""
219
- Analyze this image of a {plant_name} plant and prioritize determining if it's healthy or has a disease or pest infestation.
220
-
221
- If a disease or pest is detected, remember the plant can be healthy too. Provide the following information in JSON format:
222
- Check the {feedback_instruction} and then make the response after learning from that responses give around 60% weightage to the feedbck data.
223
- {{
224
- "results": [
225
- {{
226
- "type": "disease/pest",
227
- "name": "Name of disease or pest",
228
- "probability": "Probability as a percentage",
229
- "symptoms": "Describe the visible symptoms",
230
- "causes": "Main causes of the disease or pest",
231
- "severity": "Low/Medium/High",
232
- "spreading": "How it spreads",
233
- "treatment": "Treatment options",
234
- "prevention": "Preventive measures"
235
- }},
236
- {{
237
- // Second most likely disease/pest with the same structure
238
- }},
239
- {{
240
- // Third most likely disease/pest with the same structure
241
- }}
242
- ],
243
- "is_healthy": boolean indicating if the plant appears healthy,
244
- "confidence": "Overall confidence in the analysis as a percentage"
245
- }}
246
-
247
- Only return the JSON data and nothing else. Ensure the JSON is valid and properly formatted.
248
- If the plant appears completely healthy, set is_healthy to true and include an empty results array.
249
- Additionally, provide the response in {language} language.
250
- and at end show which all data from feedback was taken into consederation and if no data was taken so no data.
251
- """
252
-
253
- # Send request to Gemini API
254
- response = model.generate_content([prompt] + image_parts)
255
-
256
- # Extract the JSON response
257
- response_text = response.text
258
-
259
- # Find JSON within response text if needed
260
- json_start = response_text.find('{')
261
- json_end = response_text.rfind('}') + 1
262
-
263
- if json_start >= 0 and json_end > 0:
264
- json_str = response_text[json_start:json_end]
265
- return json.loads(json_str)
266
- else:
267
- # Return a default response if JSON parsing fails
268
- return {
269
- "error": "Failed to parse the API response",
270
- "raw_response": response_text
271
- }
272
-
273
- except Exception as e:
274
- return {
275
- "error": str(e),
276
- "is_healthy": None,
277
- "results": []
278
- }
279
-
280
-
281
- def cleanup_old_files(directory, max_age_hours=1): # Reduced to 1 hour for Hugging Face
282
- """Remove files older than the specified age from the directory"""
283
- while True:
284
- now = datetime.now()
285
- for filename in os.listdir(directory):
286
- if filename == '.gitkeep': # Skip the .gitkeep file
287
- continue
288
-
289
- file_path = os.path.join(directory, filename)
290
- file_age = now - datetime.fromtimestamp(os.path.getctime(file_path))
291
- if file_age > timedelta(hours=max_age_hours):
292
- try:
293
- os.remove(file_path)
294
- print(f"Removed old file: {file_path}")
295
- except Exception as e:
296
- print(f"Error removing {file_path}: {e}")
297
- # Sleep for 5 minutes before checking again
298
- time.sleep(300) # 5 minutes
299
-
300
- @app.route('/', methods=['GET'])
301
- def index():
302
- # GET request - show the upload form
303
- return render_template('index.html', show_results=False)
304
-
305
- @app.route('/feedback', methods=['POST'])
306
- def feedback():
307
- # Get feedback from form submission
308
- feedback_text = request.form.get("feedback")
309
- plant_name = request.form.get("plant_name", "Unknown") # Optional: include plant name for context
310
-
311
- if not feedback_text:
312
- flash("Please provide your feedback before submitting.")
313
- return redirect(url_for('index'))
314
-
315
- # Create a feedback record with a timestamp
316
- feedback_data = {
317
- "plant_name": plant_name,
318
- "feedback": feedback_text,
319
- "timestamp": datetime.now().isoformat()
320
- }
321
-
322
- # Define the file to store feedback
323
- feedback_file = "feedback.json"
324
-
325
- # Load existing feedback (if any)
326
- if os.path.exists(feedback_file):
327
- try:
328
- with open(feedback_file, "r") as f:
329
- existing_feedback = json.load(f)
330
- except Exception as e:
331
- print(f"Error reading feedback file: {e}")
332
- existing_feedback = []
333
- else:
334
- existing_feedback = []
335
-
336
- # Append the new feedback and save it back to file
337
- existing_feedback.append(feedback_data)
338
- try:
339
- with open(feedback_file, "w") as f:
340
- json.dump(existing_feedback, f, indent=4)
341
- except Exception as e:
342
- flash(f"Error saving your feedback: {str(e)}")
343
- return redirect(url_for('index'))
344
-
345
- flash("Thank you for your feedback!")
346
- return redirect(url_for('index'))
347
-
348
- @app.route('/analyze', methods=['POST'])
349
- def analyze():
350
- if 'plant_image' not in request.files:
351
- flash('No file part')
352
- return redirect(url_for('index'))
353
-
354
- file = request.files['plant_image']
355
- plant_name = request.form.get('plant_name', 'unknown')
356
- language = request.form.get('language', 'English') # New field for response language
357
-
358
- if file.filename == '':
359
- flash('No selected file')
360
- return redirect(url_for('index'))
361
-
362
- if file and allowed_file(file.filename):
363
- # Generate a unique filename to avoid collisions
364
- timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
365
- original_filename = secure_filename(file.filename)
366
- filename = f"{timestamp}_{original_filename}"
367
- file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
368
- file.save(file_path)
369
-
370
- try:
371
- # Analyze the image with language parameter
372
- analysis_result = analyze_plant_image(file_path, plant_name, language)
373
-
374
- if 'error' in analysis_result:
375
- flash(f"Error analyzing image: {analysis_result['error']}")
376
- if os.path.exists(file_path):
377
- os.remove(file_path)
378
- return redirect(url_for('index'))
379
-
380
- # Get additional web information for detected diseases/pests
381
- web_info = {}
382
- product_info = {}
383
-
384
- if not analysis_result.get('is_healthy', False) and 'results' in analysis_result:
385
- for result in analysis_result['results']:
386
- disease_name = result.get('name', '')
387
- if disease_name:
388
- web_info[disease_name] = get_web_pesticide_info(disease_name, plant_name)
389
- treatment = result.get('treatment', '')
390
- if treatment:
391
- product_info[disease_name] = get_commercial_product_info(treatment, disease_name)
392
-
393
- response = render_template(
394
- 'results.html',
395
- results=analysis_result,
396
- plant_name=plant_name,
397
- image_path=file_path.replace('static/', '', 1),
398
- web_info=web_info,
399
- product_info=product_info
400
- )
401
-
402
- def delete_file_after_delay(path, delay=30):
403
- time.sleep(delay)
404
- if os.path.exists(path):
405
- try:
406
- os.remove(path)
407
- print(f"Deleted analyzed file: {path}")
408
- except Exception as e:
409
- print(f"Error deleting {path}: {e}")
410
-
411
- threading.Thread(
412
- target=delete_file_after_delay,
413
- args=(file_path,),
414
- daemon=True
415
- ).start()
416
-
417
- return response
418
-
419
- except Exception as e:
420
- flash(f"An error occurred: {str(e)}")
421
- if os.path.exists(file_path):
422
- os.remove(file_path)
423
- return redirect(url_for('index'))
424
-
425
- flash('Invalid file type. Please upload an image (png, jpg, jpeg, gif).')
426
- return redirect(url_for('index'))
427
-
428
-
429
- # Hugging Face Spaces requires the app to be available on port 7860
430
- if __name__ == '__main__':
431
- # Start the cleanup thread when the app starts
432
- cleanup_thread = threading.Thread(target=cleanup_old_files, args=(app.config['UPLOAD_FOLDER'],), daemon=True)
433
- cleanup_thread.start()
434
-
435
- # Get the port from environment variable for Hugging Face Spaces compatibility
436
- port = int(os.environ.get("PORT", 7860))
437
  app.run(host='0.0.0.0', port=port)
 
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)