Al1Abdullah commited on
Commit
47058bc
·
1 Parent(s): df30d59

Deploy Plantoi: Update app, requirements, and model

Browse files
.gitattributes CHANGED
@@ -1,3 +1,4 @@
 
1
  *.jpg filter=lfs diff=lfs merge=lfs -text
2
  *.webp filter=lfs diff=lfs merge=lfs -text
3
  *.png filter=lfs diff=lfs merge=lfs -text
 
1
+ model/*.h5 filter=lfs diff=lfs merge=lfs -text
2
  *.jpg filter=lfs diff=lfs merge=lfs -text
3
  *.webp filter=lfs diff=lfs merge=lfs -text
4
  *.png filter=lfs diff=lfs merge=lfs -text
app.py ADDED
@@ -0,0 +1,598 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import pickle
3
+ import json
4
+ import re
5
+ import numpy as np
6
+ from bytez import Bytez # Import openai
7
+ from flask import Flask, request, jsonify, render_template, session, Response, stream_with_context
8
+ from PIL import Image
9
+ from disease_data import DISEASE_INFO
10
+ from showcase_data import SHOWCASE_DATA
11
+ import io
12
+ from tensorflow.keras.models import load_model
13
+ from tensorflow.keras.preprocessing.image import img_to_array
14
+ from werkzeug.utils import secure_filename
15
+ import matplotlib.pyplot as plt
16
+ from matplotlib.gridspec import GridSpec
17
+ import seaborn as sns
18
+ import base64
19
+ from io import BytesIO
20
+ import cv2
21
+ import threading
22
+
23
+ sdk = Bytez('4d16a7e053afc613fc7f85b460549ef9')
24
+
25
+ # --- Matplotlib Configuration ---
26
+ plt.switch_backend('Agg')
27
+
28
+ # --- Configuration ---
29
+ os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
30
+
31
+ # --- Flask App Initialization ---
32
+ app = Flask(__name__)
33
+ app.secret_key = 'plantoi_secret'
34
+ app.config['UPLOAD_FOLDER'] = 'static/uploads'
35
+ os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
36
+
37
+ # --- AI Model Loading ---
38
+ model = None
39
+ class_indices = None
40
+ class_names = None
41
+
42
+ def load_ai_models():
43
+ """Load Keras model and class indices in a background thread."""
44
+ global model, class_indices, class_names
45
+ try:
46
+ print("Loading Keras model in background thread...")
47
+ model = load_model('model/plant_disease_model_final.h5')
48
+ with open('data/class_indices.pkl', 'rb') as f:
49
+ class_indices = pickle.load(f)
50
+ class_names = {v: k for k, v in class_indices.items()}
51
+ print("✅ Keras model, class indices, and disease info loaded successfully.")
52
+ except Exception as e:
53
+ model = None
54
+ class_indices = None
55
+ class_names = None
56
+ print(f"❌ Error loading Keras model or class indices: {e}")
57
+
58
+ # Start background loading so the Flask server can start immediately
59
+ loader_thread = threading.Thread(target=load_ai_models, daemon=True)
60
+ loader_thread.start()
61
+
62
+ # --- Helper Functions ---
63
+ def preprocess_image(image_bytes):
64
+ try:
65
+ img = Image.open(io.BytesIO(image_bytes)).convert('RGB')
66
+ img = img.resize((224, 224))
67
+ img_array = img_to_array(img)
68
+ img_array = np.expand_dims(img_array, axis=0)
69
+ img_array /= 255.0
70
+ return img_array
71
+ except Exception as e:
72
+ print(f"Error in preprocessing image: {e}")
73
+ return None
74
+
75
+ def analyze_image_with_moondream(image_bytes):
76
+ """
77
+ Analyzes an image using a multimodal model via Bytez Cloud API and returns the raw text response.
78
+ NOTE: Multimodal support with the native Bytez SDK is not yet directly integrated here.
79
+ """
80
+ return "Image analysis using the native Bytez SDK is not yet supported. Please contact support for multimodal model integration."
81
+
82
+ # --- Flask Routes ---
83
+ @app.route('/')
84
+ def index():
85
+ return render_template('index.html')
86
+
87
+ @app.route('/api/classes')
88
+ def get_model_classes():
89
+ if class_names:
90
+ # Use the keys from DISEASE_INFO as the source of truth for display and IDs
91
+ # This ensures consistency with what the user has defined as the model's output
92
+ sorted_classes = sorted(list(DISEASE_INFO.keys()))
93
+ return jsonify(sorted_classes)
94
+ return jsonify({"error": "Class names not available or model not loaded."}), 500
95
+
96
+ @app.route('/api/showcase/<path:class_id>')
97
+ def get_showcase_data(class_id):
98
+ """Returns the showcase data for a specific class."""
99
+ print(f"Requested Class ID: {class_id}") # Debug print statement as requested
100
+ data = SHOWCASE_DATA.get(class_id)
101
+ if data:
102
+ return jsonify(data)
103
+
104
+ print(f"DEBUG: Failed to find '{class_id}' in SHOWCASE_DATA.")
105
+ return jsonify({"error": f"Class ID '{class_id}' not found in showcase data."}), 404
106
+
107
+ @app.route('/diagnose', methods=['POST'])
108
+ def diagnose():
109
+ if 'file' not in request.files:
110
+ return jsonify({"error": "No file part in the request. The key should be 'file'."}), 400
111
+
112
+ file = request.files['file']
113
+ if file.filename == '':
114
+ return jsonify({"error": "No selected file."}), 400
115
+
116
+ filename = secure_filename(file.filename)
117
+ filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
118
+ file.save(filepath)
119
+
120
+ # Store absolute path in session for visualization
121
+ session['current_img_path'] = os.path.abspath(filepath)
122
+
123
+ file.seek(0)
124
+ image_bytes = file.read()
125
+
126
+ if model is None or class_names is None:
127
+ return jsonify({"error": "AI models are not loaded. Please check server logs."}), 500
128
+
129
+ processed_image = preprocess_image(image_bytes)
130
+ if processed_image is None:
131
+ return jsonify({"error": "Failed to preprocess image."}), 400
132
+
133
+ # Step A: Get Prediction & Confidence
134
+ prediction = model.predict(processed_image)
135
+
136
+ # Store prediction data in session
137
+ session['last_prediction'] = prediction[0].tolist()
138
+ session['last_confidence'] = float(np.max(prediction))
139
+
140
+ confidence = np.max(prediction)
141
+ predicted_class_index = np.argmax(prediction)
142
+ predicted_class_name = class_names.get(predicted_class_index, "Unknown")
143
+ # Add debugging print statement
144
+ print(f"DEBUG: Model predicted '{predicted_class_name}' (Confidence: {confidence:.2f})")
145
+
146
+ # Step B (Fast Lane)
147
+ report_data = DISEASE_INFO.get(predicted_class_name)
148
+ if confidence > 0.55 and report_data:
149
+ print(f"Fast Lane: Confidence > 55% ({confidence:.2f}). Found '{predicted_class_name}' in DB.")
150
+ report = {
151
+ "disease_name": predicted_class_name.replace('___', ' ').replace('_', ' '),
152
+ "health_status": f"{report_data['status']}. {report_data['description']}",
153
+ "detailed_symptoms": "\n".join([f"- {s}" for s in report_data['symptoms']]),
154
+ "prevention_methods": "\n".join([f"- {p}" for p in report_data['prevention']]),
155
+ "smart_analysis": "Diagnosis from internal knowledge base. High confidence.",
156
+ "confidence": float(confidence),
157
+ "source_model": "Keras + DB"
158
+ }
159
+ session['last_diagnosis'] = report
160
+ return jsonify(report)
161
+
162
+ # Step C (Unknown Lane - Simplified)
163
+ print(f"Unknown Lane: Confidence <= 55% or class not in DB. Using Moondream for text analysis.")
164
+ ai_text_response = analyze_image_with_moondream(image_bytes)
165
+
166
+ report = {
167
+ "disease_name": "AI Visual Analysis",
168
+ "health_status": "See analysis below.",
169
+ "detailed_symptoms": ai_text_response, # The entire AI response is placed here
170
+ "prevention_methods": "Please refer to the analysis above.",
171
+ "smart_analysis": "Analysis provided by Moondream AI. This is a general analysis and may not be as specific as a diagnosis for a known plant type.",
172
+ "confidence": float(confidence), # Still show Keras confidence for context
173
+ "source_model": 'Moondream AI'
174
+ }
175
+
176
+ session['last_diagnosis'] = report
177
+ return jsonify(report)
178
+
179
+ @app.route('/chat', methods=['POST'])
180
+ def chat():
181
+ user_message = request.json.get('message')
182
+ if not user_message:
183
+ return jsonify({'error': 'No message provided'}), 400
184
+
185
+ # Initialize chat history in session if it doesn't exist
186
+ if 'chat_history' not in session:
187
+ session['chat_history'] = []
188
+
189
+ # Retrieve the last diagnosis context from the session
190
+ last_diagnosis = session.get('last_diagnosis', {})
191
+ diagnosis_context = ""
192
+ if last_diagnosis:
193
+ diagnosis_context = f"""
194
+ Here is the last plant diagnosis result:
195
+ Disease Name: {last_diagnosis.get('disease_name', 'N/A')}
196
+ Health Status: {last_diagnosis.get('health_status', 'N/A')}
197
+ Detailed Symptoms: {last_diagnosis.get('detailed_symptoms', 'N/A')}
198
+ Prevention Methods: {last_diagnosis.get('prevention_methods', 'N/A')}
199
+ Smart Analysis: {last_diagnosis.get('smart_analysis', 'N/A')}
200
+ Confidence: {last_diagnosis.get('confidence', 'N/A') * 100:.2f}%
201
+ Source Model: {last_diagnosis.get('source_model', 'N/A')}
202
+ """
203
+
204
+ # Construct a system message for the AI
205
+ system_message_content = f"""You are a professional Botanical Expert.
206
+ Rule 1: If the user asks a simple question (e.g., 'Hi', 'How are you?'), reply with a warm, concise 1-sentence response.
207
+ Rule 2: If the user asks a technical or complex question, provide a structured response with Headings (##), Bold text for key terms, and Bullet points for steps.
208
+ Rule 3: Never be too long or too short; maintain a balanced, helpful tone.
209
+ {diagnosis_context}
210
+ Please answer the user's question. If the question is related to the diagnosis, use the provided context.
211
+ If the context is not sufficient, mention that.
212
+ """
213
+
214
+ # Append user's message to chat history
215
+ session['chat_history'].append({'role': 'user', 'content': user_message})
216
+
217
+ # Create the full messages list to send to Ollama
218
+ # Ensure system message is always the first.
219
+ messages_to_send = [{"role": "system", "content": system_message_content}] + session['chat_history'][-10:] # Keep last 10 chat messages
220
+
221
+
222
+ def generate_bytez_responses():
223
+ full_ai_response_content = ""
224
+ model_id = 'avans06/Meta-Llama-3.2-8B-Instruct' # New Bytez model ID
225
+ print(f"Using Model: {model_id}") # Log model ID
226
+ try:
227
+ # Prepare messages as a list of dictionaries with roles for the Bytez SDK
228
+ # The first message is the system prompt (rules + diagnosis context)
229
+ messages_for_sdk = [
230
+ {"role": "system", "content": system_message_content}
231
+ ]
232
+ # Append previous chat history messages (excluding the system message already added)
233
+ for msg in session['chat_history'][-9:]: # Keep last 9 messages + current user message
234
+ # Ensure the message role is 'user' or 'assistant'
235
+ if msg['role'] in ['user', 'assistant']:
236
+ messages_for_sdk.append({"role": msg['role'], "content": msg['content']})
237
+
238
+ # Add the current user message as the last entry
239
+ # The current user_message is already added to session['chat_history'] above,
240
+ # so it should be included in the loop, or we add the latest one explicitly.
241
+ # Let's adjust the loop to include up to the current user message.
242
+ # Re-evaluating the session['chat_history'] logic: it contains ALL messages.
243
+ # So, `messages_to_send` (which includes system message and relevant history)
244
+ # is actually better as a base.
245
+
246
+ messages_for_sdk = []
247
+ messages_for_sdk.append({"role": "system", "content": system_message_content})
248
+ for msg in session['chat_history'][-10:]: # Get last 10 messages from history
249
+ messages_for_sdk.append({"role": msg['role'], "content": msg['content']})
250
+
251
+ llm_model = sdk.model(model_id)
252
+ result = llm_model.run(messages_for_sdk) # Pass structured messages directly
253
+
254
+ if result.error: # Check for error in result
255
+ print(f'Bytez SDK Error: {result.error}')
256
+ error_msg = f'AI Error: {str(result.error)}' # Capture error as a string
257
+ yield error_msg.encode('utf-8') # Yield specific error message
258
+ return # Stop generation on error
259
+
260
+ if isinstance(result.output, dict):
261
+ content = result.output.get('content', '')
262
+ else:
263
+ content = str(result.output) # Extract the string content
264
+ if content is not None: # Safety check before encoding
265
+ full_ai_response_content = content
266
+ yield content.encode('utf-8') # Encode for Flask streaming
267
+ else:
268
+ yield "Error: Bytez SDK returned no output.".encode('utf-8')
269
+
270
+ except Exception as e:
271
+ app.logger.error(f"Bytez SDK error in chat: {e}", exc_info=True)
272
+ yield f"Error: An unexpected error occurred with the Bytez SDK. Details: {e}".encode('utf-8') # Encode error messages too
273
+ finally:
274
+ if full_ai_response_content:
275
+ session['chat_history'].append({'role': 'assistant', 'content': full_ai_response_content})
276
+ session['chat_history'] = session['chat_history'][-10:]
277
+
278
+
279
+ return Response(stream_with_context(generate_bytez_responses()), mimetype='text/plain')
280
+
281
+ @app.route('/visualize', methods=['GET'])
282
+ def visualize():
283
+ # Retrieve the image path from session
284
+ filepath = session.get('current_img_path', '')
285
+
286
+ if not filepath or filepath == '':
287
+ return jsonify({"error": "No image has been diagnosed yet."}), 400
288
+
289
+ if not os.path.exists(filepath):
290
+ return jsonify({"error": "Could not find the diagnosed image file."}), 404
291
+
292
+ # --- Retrieve data from session ---
293
+ prediction = np.array(session.get('last_prediction', []))
294
+ top_confidence = session.get('last_confidence', 0)
295
+ predicted_class_index = np.argmax(prediction) if prediction.size > 0 else 0
296
+ predicted_class_name = class_names.get(predicted_class_index, "Unknown").replace('___', ' ').replace('_', ' ')
297
+
298
+ if prediction.size == 0:
299
+ return jsonify({"error": "Prediction data not found in session."}), 400
300
+
301
+ # --- Advanced Image Processing ---
302
+ try:
303
+ img = Image.open(filepath).convert('RGB')
304
+ img_np = np.array(img)
305
+ original_height, original_width = img_np.shape[:2]
306
+
307
+ # Define "Healthy Green" as pixels where G > R + 15 and G > B + 15
308
+ healthy_mask = (img_np[:, :, 1] > img_np[:, :, 0] + 15) & (img_np[:, :, 1] > img_np[:, :, 2] + 15)
309
+ healthy_pixels = np.sum(healthy_mask)
310
+ total_pixels = img_np.shape[0] * img_np.shape[1]
311
+ necrotic_infected_pixels = total_pixels - healthy_pixels
312
+
313
+ healthy_ratio = healthy_pixels / total_pixels if total_pixels > 0 else 0.0
314
+ necrotic_ratio = necrotic_infected_pixels / total_pixels if total_pixels > 0 else 0.0
315
+
316
+ # Advanced disease region detection using color analysis
317
+ # Detect brown/yellow regions (common disease indicators)
318
+ # Convert RGB to BGR for OpenCV, then to HSV
319
+ img_bgr = cv2.cvtColor(img_np, cv2.COLOR_RGB2BGR)
320
+ hsv_img = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2HSV)
321
+ brown_mask = (hsv_img[:, :, 0] >= 10) & (hsv_img[:, :, 0] <= 30) & (hsv_img[:, :, 1] >= 50)
322
+ yellow_mask = (hsv_img[:, :, 0] >= 20) & (hsv_img[:, :, 0] <= 30) & (hsv_img[:, :, 2] >= 100)
323
+ disease_regions = np.logical_or(brown_mask, yellow_mask)
324
+ disease_pixel_ratio = np.sum(disease_regions) / total_pixels if total_pixels > 0 else 0.0
325
+
326
+ # Calculate health score (0-100): based on healthy pixels, confidence, and disease regions
327
+ is_healthy_class = 'Healthy' in predicted_class_name
328
+ base_score = healthy_ratio * 100
329
+ confidence_bonus = top_confidence * 20 if is_healthy_class else 0
330
+ disease_penalty = disease_pixel_ratio * 30
331
+ health_score = min(100, max(0, base_score + confidence_bonus - disease_penalty))
332
+
333
+ # Calculate severity level
334
+ if health_score >= 80:
335
+ severity = "Low"
336
+ severity_color = "#22c55e"
337
+ elif health_score >= 50:
338
+ severity = "Moderate"
339
+ severity_color = "#f59e0b"
340
+ else:
341
+ severity = "High"
342
+ severity_color = "#ef4444"
343
+
344
+ # Calculate pigmentation ratios (RGB channel ratios)
345
+ red_ratio = np.mean(img_np[:, :, 0]) / 255.0
346
+ green_ratio = np.mean(img_np[:, :, 1]) / 255.0
347
+ blue_ratio = np.mean(img_np[:, :, 2]) / 255.0
348
+
349
+ # Normalize RGB ratios for pie chart
350
+ rgb_total = red_ratio + green_ratio + blue_ratio
351
+ if rgb_total > 0:
352
+ red_pct = red_ratio / rgb_total
353
+ green_pct = green_ratio / rgb_total
354
+ blue_pct = blue_ratio / rgb_total
355
+ else:
356
+ red_pct, green_pct, blue_pct = 0.33, 0.33, 0.34
357
+
358
+ # Calculate color variance (pigmentation variation - indicates disease spots)
359
+ color_variance = np.var(img_np.reshape(-1, 3), axis=0)
360
+ var_total = np.sum(color_variance)
361
+ if var_total > 0:
362
+ var_red = color_variance[0] / var_total
363
+ var_green = color_variance[1] / var_total
364
+ var_blue = color_variance[2] / var_total
365
+ else:
366
+ var_red, var_green, var_blue = 0.33, 0.33, 0.34
367
+
368
+ # Calculate edge density (diseased leaves often have more defined edges/patterns)
369
+ # Convert RGB to BGR for OpenCV, then to grayscale
370
+ img_bgr = cv2.cvtColor(img_np, cv2.COLOR_RGB2BGR)
371
+ gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY)
372
+ edges = cv2.Canny(gray, 50, 150)
373
+ edge_density = np.sum(edges > 0) / total_pixels if total_pixels > 0 else 0.0
374
+
375
+ # Calculate chlorophyll index approximation (healthy leaves have higher green values)
376
+ chlorophyll_index = green_ratio - 0.3 # Baseline adjustment
377
+ chlorophyll_index = max(0, min(1, chlorophyll_index))
378
+
379
+ except Exception as e:
380
+ print(f"Error during pixel analysis: {e}")
381
+ healthy_ratio, necrotic_ratio = 0.5, 0.5
382
+ disease_pixel_ratio = 0.3
383
+ health_score = 50.0
384
+ severity = "Unknown"
385
+ severity_color = "#6b7280"
386
+ red_pct, green_pct, blue_pct = 0.33, 0.33, 0.34
387
+ var_red, var_green, var_blue = 0.33, 0.33, 0.34
388
+ edge_density = 0.1
389
+ chlorophyll_index = 0.5
390
+ img_np = np.zeros((224, 224, 3), dtype=np.uint8)
391
+
392
+ # --- Premium Plotting Engine ---
393
+ try:
394
+ plt.style.use('dark_background')
395
+ sns.set_palette("husl")
396
+ fig = plt.figure(figsize=(20, 12), dpi=150)
397
+ fig.patch.set_alpha(0.0)
398
+ fig.suptitle('Plant Health Analysis Dashboard', color='white', fontsize=20, fontweight='bold', y=0.98)
399
+
400
+ # Create a 2x3 grid layout
401
+ gs = GridSpec(2, 2, figure=fig, hspace=0.6, wspace=0.4, left=0.08, right=0.92, top=0.92, bottom=0.08)
402
+
403
+ # Chart 1: Health Metrics Analysis (Top Left)
404
+ ax1 = fig.add_subplot(gs[0, 0])
405
+ ax1.set_facecolor('none')
406
+
407
+ # Create severity indicators with visual bars
408
+ metrics = [
409
+ ('Healthy Tissue', healthy_ratio * 100, '#22c55e'),
410
+ ('Disease Regions', disease_pixel_ratio * 100, '#ef4444'),
411
+ ('Necrotic Areas', necrotic_ratio * 100, '#dc2626'),
412
+ ('Edge Density', edge_density * 100, '#8b5cf6'),
413
+ ('Chlorophyll Index', chlorophyll_index * 100, '#10b981')
414
+ ]
415
+
416
+ y_pos = np.arange(len(metrics))
417
+ bars = ax1.barh(y_pos, [m[1] for m in metrics], color=[m[2] for m in metrics],
418
+ alpha=0.8, edgecolor='white', linewidth=1.5, height=0.6)
419
+ ax1.set_yticks(y_pos)
420
+ ax1.set_yticklabels([m[0] for m in metrics], color='white', fontsize=10, fontweight='bold')
421
+ ax1.set_xlabel('Percentage (%)', color='white', fontweight='bold', fontsize=11)
422
+ ax1.set_title('Health Metrics Analysis', color='white', fontweight='bold', fontsize=13, pad=10)
423
+ ax1.set_xlim(0, 100)
424
+ ax1.tick_params(axis='x', colors='white')
425
+ ax1.grid(True, alpha=0.2, color='white', axis='x')
426
+
427
+ # Add value labels
428
+ for bar, val in zip(bars, [m[1] for m in metrics]):
429
+ width = bar.get_width()
430
+ ax1.text(width + 2, bar.get_y() + bar.get_height()/2, f'{val:.1f}%',
431
+ ha='left', va='center', color='white', fontsize=9, fontweight='bold')
432
+
433
+ # Chart 2: Color Variance Heatmap (Top Center)
434
+ ax2 = fig.add_subplot(gs[0, 1])
435
+ ax2.set_facecolor('none')
436
+ # Create a color variance map
437
+ if img_np.shape[0] > 0 and img_np.shape[1] > 0:
438
+ # Calculate local variance in a grid
439
+ grid_size = 20
440
+ h_step = max(1, img_np.shape[0] // grid_size)
441
+ w_step = max(1, img_np.shape[1] // grid_size)
442
+ variance_map = np.zeros((grid_size, grid_size))
443
+
444
+ for i in range(grid_size):
445
+ for j in range(grid_size):
446
+ y_start, y_end = i * h_step, min((i + 1) * h_step, img_np.shape[0])
447
+ x_start, x_end = j * w_step, min((j + 1) * w_step, img_np.shape[1])
448
+ region = img_np[y_start:y_end, x_start:x_end]
449
+ if region.size > 0:
450
+ variance_map[i, j] = np.var(region)
451
+
452
+ im = ax2.imshow(variance_map, cmap='RdYlGn_r', aspect='auto', interpolation='bilinear')
453
+ ax2.set_title('Color Variance Heatmap\n(Disease Hotspots)', color='white', fontweight='bold', fontsize=12, pad=10)
454
+ ax2.set_xlabel('Horizontal Position', color='white', fontsize=10)
455
+ ax2.set_ylabel('Vertical Position', color='white', fontsize=10)
456
+ ax2.tick_params(colors='white', labelsize=8)
457
+ plt.colorbar(im, ax=ax2, label='Variance', pad=0.02)
458
+
459
+
460
+
461
+ # Chart 4: Tissue Health Composition (Bottom Left)
462
+ ax4 = fig.add_subplot(gs[1, 0])
463
+ ax4.set_facecolor('none')
464
+ # Enhanced pie chart with donut style
465
+ sizes = [healthy_ratio * 100, disease_pixel_ratio * 100, necrotic_ratio * 100]
466
+ labels = ['Healthy', 'Disease Regions', 'Necrotic']
467
+ colors_pie = ['#22c55e', '#f59e0b', '#ef4444']
468
+ explode = (0.05, 0.05, 0.05)
469
+
470
+ wedges, texts, autotexts = ax4.pie(sizes, labels=labels, colors=colors_pie, autopct='%1.1f%%',
471
+ startangle=90, explode=explode, shadow=True,
472
+ textprops={'color': 'white', 'weight': 'bold', 'fontsize': 10},
473
+ wedgeprops=dict(width=0.5, edgecolor='white', linewidth=2))
474
+ ax4.set_title('Tissue Health Composition', color='white', fontweight='bold', fontsize=12, pad=10)
475
+
476
+ # Chart 5: Spider Web Chart - Plant Health Risk Classification (Bottom Center & Right)
477
+ ax5 = fig.add_subplot(gs[1, 1], projection='polar')
478
+ ax5.set_facecolor('none')
479
+ ax5.patch.set_alpha(0.0)
480
+
481
+ # Calculate risk level based on multiple factors
482
+ # Factors for risk assessment
483
+ factors = {
484
+ 'Health Score': health_score,
485
+ 'Disease Coverage': 100 - (disease_pixel_ratio * 100),
486
+ 'Chlorophyll': chlorophyll_index * 100,
487
+ 'Tissue Integrity': healthy_ratio * 100,
488
+ 'Disease Severity': 100 - (necrotic_ratio * 100),
489
+ 'Recovery Potential': max(0, 100 - (disease_pixel_ratio * 100) - (necrotic_ratio * 100))
490
+ }
491
+
492
+ # Determine overall risk category
493
+ avg_health = np.mean(list(factors.values()))
494
+ if avg_health >= 80:
495
+ risk_category = "Can Improve"
496
+ risk_color = '#22c55e'
497
+ risk_level = 1 # Low risk
498
+ elif avg_health >= 60:
499
+ risk_category = "Stable / Less Dangerous"
500
+ risk_color = '#f59e0b'
501
+ risk_level = 2 # Moderate risk
502
+ elif avg_health >= 30:
503
+ risk_category = "Extremely Dangerous"
504
+ risk_color = '#ef4444'
505
+ risk_level = 3 # High risk
506
+ else:
507
+ risk_category = "Totally Dead Plant"
508
+ risk_color = '#7f1d1d'
509
+ risk_level = 4 # Critical risk
510
+
511
+ # Prepare data for radar chart
512
+ categories = list(factors.keys())
513
+ values = list(factors.values())
514
+ N = len(categories)
515
+
516
+ # Compute angle for each category
517
+ angles = [n / float(N) * 2 * np.pi for n in range(N)]
518
+ angles += angles[:1] # Complete the circle
519
+ values += values[:1]
520
+
521
+ # Plot the spider web
522
+ ax5.plot(angles, values, 'o-', linewidth=3, color=risk_color, alpha=0.8, label='Current Status')
523
+ ax5.fill(angles, values, alpha=0.25, color=risk_color)
524
+
525
+ # Add reference zones for different risk levels
526
+ # Zone 1: Can Improve (green zone - 80-100)
527
+ zone1_angles = np.linspace(0, 2*np.pi, 100)
528
+ zone1_values = [80] * 100
529
+ ax5.plot(zone1_angles, zone1_values, '--', linewidth=1, color='#22c55e', alpha=0.3, label='Can Improve (80+)')
530
+ ax5.fill_between(zone1_angles, [80]*100, [100]*100, alpha=0.1, color='#22c55e')
531
+
532
+ # Zone 2: Stable (yellow zone - 60-80)
533
+ zone2_values = [60] * 100
534
+ ax5.plot(zone1_angles, zone2_values, '--', linewidth=1, color='#f59e0b', alpha=0.3, label='Stable (60-80)')
535
+ ax5.fill_between(zone1_angles, [60]*100, [80]*100, alpha=0.1, color='#f59e0b')
536
+
537
+ # Zone 3: Dangerous (red zone - 30-60)
538
+ zone3_values = [30] * 100
539
+ ax5.plot(zone1_angles, zone3_values, '--', linewidth=1, color='#ef4444', alpha=0.3, label='Dangerous (30-60)')
540
+ ax5.fill_between(zone1_angles, [30]*100, [60]*100, alpha=0.1, color='#ef4444')
541
+
542
+ # Zone 4: Dead (dark red zone - 0-30)
543
+ ax5.fill_between(zone1_angles, [0]*100, [30]*100, alpha=0.1, color='#7f1d1d', label='Dead (0-30)')
544
+
545
+ # Set labels
546
+ ax5.set_xticks(angles[:-1])
547
+ ax5.set_xticklabels(categories, color='white', fontsize=10, fontweight='bold')
548
+ ax5.set_ylim(0, 100)
549
+ ax5.set_yticks([25, 50, 75, 100])
550
+ ax5.set_yticklabels(['25', '50', '75', '100'], color='white', fontsize=8)
551
+ ax5.grid(True, linestyle='--', linewidth=1, alpha=0.3, color='white')
552
+ ax5.set_theta_offset(np.pi / 2)
553
+ ax5.set_theta_direction(-1)
554
+
555
+ # Add title with risk category
556
+ ax5.set_title(f'Plant Health Risk Assessment\nStatus: {risk_category}',
557
+ color=risk_color, fontweight='bold', fontsize=14, pad=20)
558
+
559
+ # Add legend
560
+ ax5.legend(loc='upper right', bbox_to_anchor=(1.05, 1),
561
+ facecolor='#141c2e', edgecolor='white', labelcolor='white', fontsize=8)
562
+
563
+ # Add value labels on the spider web
564
+ for angle, value, category in zip(angles[:-1], values[:-1], categories):
565
+ ax5.text(angle, value + 5, f'{value:.0f}%',
566
+ ha='center', va='center', color='white',
567
+ fontsize=9, fontweight='bold', bbox=dict(boxstyle='round,pad=0.3',
568
+ facecolor=risk_color, alpha=0.7, edgecolor='white', linewidth=1))
569
+
570
+ # Add diagnosis info text at the bottom
571
+ diagnosis_text = f"Diagnosis: {predicted_class_name[:40]} | Confidence: {top_confidence*100:.1f}% | Model: Keras CNN"
572
+ fig.text(0.5, 0.01, diagnosis_text, ha='center', va='bottom',
573
+ color='white', fontsize=10, style='italic', alpha=0.8)
574
+
575
+ # --- Save to Buffer and Return ---
576
+ buf = BytesIO()
577
+ fig.savefig(buf, format='png', transparent=True, dpi=120, bbox_inches='tight')
578
+ buf.seek(0)
579
+
580
+ img_b64 = base64.b64encode(buf.getvalue()).decode('utf-8')
581
+ graph_url = f"data:image/png;base64,{img_b64}"
582
+
583
+ plt.close(fig) # Close figure to free memory
584
+
585
+ return jsonify({"graph": graph_url})
586
+
587
+ except Exception as e:
588
+ print(f"Error during visualization generation: {e}")
589
+ import traceback
590
+ traceback.print_exc()
591
+ # Ensure any open figures are closed
592
+ plt.close('all')
593
+ return jsonify({"error": f"Failed to generate visualization: {str(e)}"}), 500
594
+
595
+ # --- Main Execution ---
596
+ if __name__ == '__main__':
597
+ # Disable the auto-reloader to avoid double-importing heavy ML libraries
598
+ app.run(host='0.0.0.0', port=7860, debug=False, use_reloader=False)
data/class_indices.pkl ADDED
Binary file (1.12 kB). View file
 
disease_data.py ADDED
@@ -0,0 +1,546 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # --- Master Disease Dictionary ---
2
+ DISEASE_INFO = {
3
+ 'Apple Apple Scab': {
4
+ 'status': 'Infected',
5
+ 'description': 'Apple Scab is a common fungal disease that affects apple and crabapple trees. It is caused by the fungus Venturia inaequalis, which thrives in cool, wet spring weather, leading to cosmetic blemishes and potential defoliation.',
6
+ 'symptoms': [
7
+ 'Olive-green or brown spots on leaves, which may become velvety.',
8
+ 'Infected leaves can become twisted, puckered, and may drop prematurely.',
9
+ 'Dark, scabby spots on fruit, which can crack and become deformed.',
10
+ 'Blossom infections can lead to fruit not developing.'
11
+ ],
12
+ 'prevention': [
13
+ 'Plant scab-resistant apple varieties whenever possible.',
14
+ 'Rake and destroy fallen leaves in autumn to reduce the amount of overwintering fungus.',
15
+ 'Prune trees annually to improve air circulation and allow foliage to dry faster.',
16
+ 'Apply appropriate fungicides during the critical period from bud break until dry weather is consistent.'
17
+ ]
18
+ },
19
+ 'Apple Apple Black rot': {
20
+ 'status': 'Infected',
21
+ 'description': 'Black Rot is a serious fungal disease of apple trees, caused by Botryosphaeria obtusa. It can infect the fruit, leaves, and branches, often gaining entry through wounds from insects, hail, or pruning.',
22
+ 'symptoms': [
23
+ 'A firm, brown rot on fruit that enlarges and turns black, often showing concentric rings.',
24
+ 'Infected fruit may shrivel into "mummies" that remain on the tree.',
25
+ 'Purple spots on leaves that enlarge into a "frogeye" pattern with a tan center.',
26
+ 'Sunken, reddish-brown cankers on branches can girdle and kill them.'
27
+ ],
28
+ 'prevention': [
29
+ 'Prune out dead or cankered wood during winter dormancy.',
30
+ 'Remove and destroy all mummified fruit to reduce fungal spores.',
31
+ 'Control insects and other pests to prevent wounds that allow fungal entry.',
32
+ 'Apply fungicides as part of a comprehensive disease management program.'
33
+ ]
34
+ },
35
+ 'Apple Cedar apple rust': {
36
+ 'status': 'Infected',
37
+ 'description': 'Cedar Apple Rust is a fungal disease caused by Gymnosporangium juniperi-virginianae. It has a complex life cycle that requires both an apple or crabapple and an Eastern Red Cedar (or juniper) to survive.',
38
+ 'symptoms': [
39
+ 'Bright, orange-yellow spots on apple leaves, often with a reddish border.',
40
+ 'Tiny black dots appear in the center of the leaf spots, followed by small, tube-like structures on the underside.',
41
+ 'Orange, rust-colored lesions on the fruit, typically near the calyx end.',
42
+ 'On the cedar host, brown, woody galls form and produce orange, gelatinous "horns" in the spring.'
43
+ ],
44
+ 'prevention': [
45
+ 'Remove nearby Eastern Red Cedar trees, which are the alternate host (if feasible).',
46
+ 'Choose apple varieties that are resistant to rust diseases.',
47
+ 'Apply fungicides to protect apple trees during the infection period, from pink bud stage through early summer.',
48
+ 'Prune and destroy cedar galls before they produce spores.'
49
+ ]
50
+ },
51
+ 'Apple Apple Healthy': {
52
+ 'status': 'Healthy',
53
+ 'description': 'The plant exhibits strong, vibrant foliage and shows no signs of fungal, bacterial, or viral distress. Its structure is sound and development appears normal for the current season.',
54
+ 'symptoms': [
55
+ 'Leaves are a uniform green color without spots, lesions, or yellowing.',
56
+ 'Stems and branches are free of cankers, galls, or discoloration.',
57
+ 'There is no sign of wilting, distortion, or premature leaf drop.'
58
+ ],
59
+ 'prevention': [
60
+ 'Continue a regimen of proper watering to avoid stress.',
61
+ 'Ensure the plant receives adequate sunlight and balanced nutrition.',
62
+ 'Regularly monitor for early signs of pests or disease.',
63
+ 'Maintain good air circulation through proper spacing and pruning.'
64
+ ]
65
+ },
66
+ 'Cherry Cherry Powdery mildew': {
67
+ 'status': 'Infected',
68
+ 'description': 'Powdery Mildew on cherry trees is caused by the fungus Podosphaera clandestina. The disease is favored by high humidity and moderate temperatures, affecting leaves, shoots, and fruit.',
69
+ 'symptoms': [
70
+ 'White, talcum-powder-like patches of fungal growth on leaves and shoots.',
71
+ 'Infected leaves may become distorted, curl upwards, and drop prematurely.',
72
+ 'Young, developing fruit can be infected, leading to russeting and misshapen cherries.',
73
+ 'New shoots may be stunted and covered in the white mildew.'
74
+ ],
75
+ 'prevention': [
76
+ 'Prune trees to promote good air circulation, which helps reduce humidity within the canopy.',
77
+ 'Avoid excessive nitrogen fertilization, as it encourages susceptible new growth.',
78
+ 'Apply fungicides, sulfur, or horticultural oils when conditions are favorable for disease development.',
79
+ 'Remove and destroy heavily infected shoots during dormant pruning.'
80
+ ]
81
+ },
82
+ 'Cherry Cherry Healthy': {
83
+ 'status': 'Healthy',
84
+ 'description': 'The cherry tree is in excellent health, showing vigorous growth appropriate for the season. The leaves are glossy and green, with no signs of mildew, shot hole, or other common ailments.',
85
+ 'symptoms': [
86
+ 'Foliage is full, with a deep green color and no spots or powdery residue.',
87
+ 'Branches are strong and free from cankers or gummy ooze.',
88
+ 'Fruit is developing normally without blemishes or deformities.'
89
+ ],
90
+ 'prevention': [
91
+ 'Protect the trunk from mechanical injury and sunscald.',
92
+ 'Ensure the tree is watered deeply but infrequently to encourage deep roots.',
93
+ 'Netting may be required to protect ripening fruit from birds.',
94
+ 'Apply dormant oil sprays to control overwintering pests.'
95
+ ]
96
+ },
97
+ 'Corn Corn Gray leaf spot': {
98
+ 'status': 'Infected',
99
+ 'description': 'Gray Leaf Spot, caused by the fungus Cercospora zeae-maydis, is a major yield-limiting disease of corn. It thrives in warm, humid conditions and survives in corn residue, making it a persistent threat.',
100
+ 'symptoms': [
101
+ 'Initial symptoms are small, tan, rectangular lesions confined by leaf veins.',
102
+ 'Lesions mature into long (2-4 inches), narrow, grayish-tan, rectangular streaks.',
103
+ 'Severe infections can cause the lesions to merge, blighting entire leaves.',
104
+ 'Symptoms typically begin on lower leaves and progress up the plant.'
105
+ ],
106
+ 'prevention': [
107
+ 'Select corn hybrids with a high tolerance or resistance rating to Gray Leaf Spot.',
108
+ 'Use crop rotation with non-host crops like soybeans or alfalfa to reduce inoculum levels.',
109
+ 'Manage crop residue with tillage where feasible to break down the fungal host material.',
110
+ 'Apply a foliar fungicide between the tasseling and early silking stages if disease is present.'
111
+ ]
112
+ },
113
+ 'Corn Corn Common rust': {
114
+ 'status': 'Infected',
115
+ 'description': 'Common Rust in corn is caused by the fungus Puccinia sorghi. While it can look dramatic, it typically develops late in the season and often has a minor impact on yield compared to other diseases.',
116
+ 'symptoms': [
117
+ 'Small, oval, cinnamon-brown pustules erupt on both upper and lower leaf surfaces.',
118
+ 'Pustules are scattered across the leaf rather than being grouped in bands.',
119
+ 'If you rub a pustule, a reddish-brown, powdery substance (spores) will come off on your finger.',
120
+ 'In severe cases, leaves can yellow and die prematurely.'
121
+ ],
122
+ 'prevention': [
123
+ 'Most modern corn hybrids have good genetic resistance to Common Rust.',
124
+ 'Fungicide applications are rarely economically justified for Common Rust alone.',
125
+ 'Planting early can help the crop mature before rust becomes severe.',
126
+ 'Monitor fields, but distinguish from the more aggressive Southern Rust.'
127
+ ]
128
+ },
129
+ 'Corn Corn Northern Leaf Blight': {
130
+ 'status': 'Infected',
131
+ 'description': 'Northern Leaf Blight (NLB) is a significant fungal disease in corn, caused by Exserohilum turcicum. It is favored by high humidity and moderate temperatures and can cause substantial yield loss.',
132
+ 'symptoms': [
133
+ 'Large, distinctive, cigar-shaped lesions on the leaves, typically 1 to 7 inches long.',
134
+ 'Lesions are tan or grayish-green and can contain dark, fuzzy areas of sporulation.',
135
+ 'The disease typically starts on lower leaves and progresses upwards.',
136
+ 'Severe infections can cause entire leaves to be blighted, reducing photosynthetic capacity.'
137
+ ],
138
+ 'prevention': [
139
+ 'Planting corn hybrids with genetic resistance to NLB is the most effective management strategy.',
140
+ 'Crop rotation and tillage can help reduce the amount of fungal inoculum in crop residue.',
141
+ 'Foliar fungicides can be effective if applied preventatively or at the first sign of disease.',
142
+ 'Avoid dense planting to promote better air circulation.'
143
+ ]
144
+ },
145
+ 'Corn Corn Healthy': {
146
+ 'status': 'Healthy',
147
+ 'description': 'The corn plant is developing well, with a strong stalk and vibrant, green leaves. There are no signs of the lesions, pustules, or discoloration associated with common corn diseases.',
148
+ 'symptoms': [
149
+ 'Leaves are broad, uniformly green, and free of spots or streaks.',
150
+ 'The stalk is sturdy and shows no signs of rot or weakness.',
151
+ 'Growth is vigorous and on track for the current stage of development.'
152
+ ],
153
+ 'prevention': [
154
+ 'Ensure a balanced fertility program, especially with adequate nitrogen.',
155
+ 'Maintain proper soil moisture through irrigation or rainfall.',
156
+ 'Scout regularly for common pests like corn earworm or stalk borer.',
157
+ 'Control weeds to reduce competition for light, water, and nutrients.'
158
+ ]
159
+ },
160
+ 'Grape Grape Black rot': {
161
+ 'status': 'Infected',
162
+ 'description': 'Black Rot, caused by the fungus Guignardia bidwellii, is one of the most destructive diseases of grapes, especially in warm, humid climates. It can infect all green parts of the plant, but is most damaging to the fruit.',
163
+ 'symptoms': [
164
+ 'Small, reddish-brown, circular lesions on leaves that develop black dots (pycnidia) in the center.',
165
+ 'Elongated, black lesions on shoots and tendrils.',
166
+ 'Infected berries develop a soft, brown spot, then turn black and shrivel into hard, wrinkled "mummies".',
167
+ 'The entire grape cluster can be destroyed in a matter of days.'
168
+ ],
169
+ 'prevention': [
170
+ 'Choose grape cultivars that are less susceptible to Black Rot.',
171
+ 'Practice diligent sanitation: remove and destroy all mummified fruit and infected canes during dormant pruning.',
172
+ 'Promote good air circulation with proper pruning, training, and leaf removal.',
173
+ 'Apply fungicides on a preventative schedule, starting before bloom and continuing through the growing season.'
174
+ ]
175
+ },
176
+ 'Grape Grape Black Measles': {
177
+ 'status': 'Infected',
178
+ 'description': 'Esca, also known as Black Measles, is a complex and destructive fungal trunk disease of grapevines. It is caused by a group of fungi that decay the wood, leading to a range of external symptoms.',
179
+ 'symptoms': [
180
+ 'On leaves of white varieties, chlorotic spots appear between veins; on red varieties, spots are red, creating a "tiger-stripe" pattern.',
181
+ 'Small, dark purple spots or specks appear on the grape berries, resembling measles.',
182
+ 'In hot weather, a portion of the vine or the entire plant may suddenly wilt and die (apoplexy).',
183
+ 'Cross-sections of infected trunks reveal a central area of soft, spongy, decayed wood.'
184
+ ],
185
+ 'prevention': [
186
+ 'There is no cure; management focuses on preventing new infections.',
187
+ 'Protect large pruning wounds with a sealant to block fungal entry.',
188
+ 'Delay pruning until late winter or early spring to allow wounds to heal faster.',
189
+ 'Remove and destroy severely symptomatic vines to reduce the spread of spores.'
190
+ ]
191
+ },
192
+ 'Grape Grape Isariopsis Leaf Spot': {
193
+ 'status': 'Infected',
194
+ 'description': 'Grape Leaf Blight, also known as Isariopsis Leaf Spot, is a fungal disease caused by Pseudocercospora vitis. While generally less damaging than Black Rot, it can cause premature defoliation in severe cases.',
195
+ 'symptoms': [
196
+ 'Irregularly shaped, dark brown or black lesions on the upper surface of mature leaves.',
197
+ 'The center of the lesions may become lighter in color as they age.',
198
+ 'On the underside of the leaf, a grayish, fuzzy mold (fungal spores) may be visible.',
199
+ 'Infected leaves may eventually yellow and drop, exposing fruit to sunscald.'
200
+ ],
201
+ 'prevention': [
202
+ 'Rake and destroy fallen leaves to reduce overwintering inoculum.',
203
+ 'Improve air circulation within the vine canopy through proper pruning techniques.',
204
+ 'Most fungicide spray programs aimed at controlling Black Rot or Downy Mildew will also manage Leaf Blight.',
205
+ 'Ensure vines are not overly stressed by drought or nutrient deficiencies.'
206
+ ]
207
+ },
208
+ 'Grape Grape Healthy': {
209
+ 'status': 'Healthy',
210
+ 'description': 'The grapevine is in excellent condition, with a full canopy of healthy leaves and developing fruit clusters. There are no signs of the spots, blights, or rots that commonly affect grapes.',
211
+ 'symptoms': [
212
+ 'Leaves are fully expanded, uniformly green, and free of lesions or powdery growth.',
213
+ 'Shoots are growing vigorously, and canes are a healthy brown color.',
214
+ 'Fruit clusters are developing well without any signs of berry shrivel or rot.'
215
+ ],
216
+ 'prevention': [
217
+ 'Continue with a preventative fungicide spray schedule as recommended for your region.',
218
+ 'Perform timely canopy management, such as shoot positioning and leaf pulling.',
219
+ 'Monitor for insect pests like grape berry moth, Japanese beetles, and spotted wing drosophila.',
220
+ 'Maintain proper irrigation and soil nutrient levels.'
221
+ ]
222
+ },
223
+ 'Citrus Citrus Black spot': {
224
+ 'status': 'Infected',
225
+ 'description': 'A fungal disease causing cosmetic damage to citrus fruit.',
226
+ 'symptoms': ['Small, hard, black, circular spots on fruit rind.'],
227
+ 'prevention': ['Apply fungicides to protect fruit.', 'Manage leaf litter.']
228
+ },
229
+ 'Citrus Citrus canker': {
230
+ 'status': 'Infected',
231
+ 'description': 'A highly contagious bacterial disease causing lesions on leaves, stems, and fruit.',
232
+ 'symptoms': ['Raised, corky lesions, often surrounded by a yellow halo.'],
233
+ 'prevention': ['Strict quarantine and eradication are primary controls.', 'Use windbreaks and copper-based bactericides.']
234
+ },
235
+ 'Citrus Citrus greening': {
236
+ 'status': 'Infected',
237
+ 'description': 'Also known as Huanglongbing (HLB), one of the most destructive diseases of citrus, transmitted by the Asian citrus psyllid.',
238
+ 'symptoms': ['Blotchy, mottled leaves (asymmetrical yellowing).', 'Fruit that is small, lopsided, and poorly colored.', 'Leads to rapid tree decline and death.'],
239
+ 'prevention': ['Aggressive control of the psyllid vector.', 'Removal of infected trees.', 'Planting disease-free nursery stock.']
240
+ },
241
+ 'Citrus Citrus Healthy': {
242
+ 'status': 'Healthy',
243
+ 'description': 'The citrus tree appears healthy.',
244
+ 'symptoms': ['No abnormalities observed.'],
245
+ 'prevention': ['Monitor for pests.', 'Provide appropriate fertilizer and good drainage.']
246
+ },
247
+ 'Peach Peach Bacterial spot': {
248
+ 'status': 'Infected',
249
+ 'description': 'Bacterial Spot, caused by the bacterium Xanthomonas arboricola pv. pruni, is a major disease of peaches, nectarines, and plums. It is most severe in regions with warm, wet, and windy spring weather.',
250
+ 'symptoms': [
251
+ 'Angular, water-soaked spots on leaves that turn purple and then brown; the centers may fall out, creating a "shot-hole" appearance.',
252
+ 'Severe leaf spotting leads to yellowing and premature defoliation, weakening the tree.',
253
+ 'Small, circular, pitted spots on the fruit surface, which can crack as the fruit expands.',
254
+ 'Twig cankers can form on new shoots, serving as an overwintering site for the bacteria.'
255
+ ],
256
+ 'prevention': [
257
+ 'Plant peach varieties with higher resistance to Bacterial Spot.',
258
+ 'Avoid planting in sandy soils, which can increase stress and susceptibility.',
259
+ 'Apply bactericides (such as copper-based products) from petal fall through the summer.',
260
+ 'Maintain tree vigor with proper fertilization and irrigation.'
261
+ ]
262
+ },
263
+ 'Peach Peach Healthy': {
264
+ 'status': 'Healthy',
265
+ 'description': 'The peach tree is exhibiting excellent health, with a full canopy of vibrant leaves and no signs of bacterial diseases or fungal issues like leaf curl. Fruit is developing well for the current season.',
266
+ 'symptoms': [
267
+ 'Leaves are a healthy green, without any spots, curling, or shot-hole patterns.',
268
+ 'Twigs and branches are free of cankers or gummy ooze.',
269
+ 'Fruit is smooth and free of spots or cracks.'
270
+ ],
271
+ 'prevention': [
272
+ 'Apply a preventative dormant season fungicide spray to control peach leaf curl.',
273
+ 'Implement a consistent spray schedule during the growing season to manage brown rot and other pests.',
274
+ 'Properly thin fruit to encourage larger size and reduce disease spread.',
275
+ 'Protect the lower trunk from borers and mechanical damage.'
276
+ ]
277
+ },
278
+ 'Bell pepper Bell pepper Bacterial spot': {
279
+ 'status': 'Infected',
280
+ 'description': 'Bacterial Spot on bell peppers is caused by several species of Xanthomonas bacteria. The disease thrives in warm, humid conditions and is easily spread by splashing water from rain or overhead irrigation.',
281
+ 'symptoms': [
282
+ 'Small, water-soaked spots on leaves that become dark brown or black and feel greasy.',
283
+ 'Infected leaves may turn yellow and drop from the plant, exposing fruit to sunscald.',
284
+ 'Raised, scab-like spots or cracks can form on the fruit, making them unmarketable.',
285
+ 'Stem lesions can also occur, appearing as dark, elongated streaks.'
286
+ ],
287
+ 'prevention': [
288
+ 'Start with certified, disease-free seeds and transplants.',
289
+ 'Rotate crops, avoiding planting peppers or tomatoes in the same location for at least a year.',
290
+ 'Use mulch and avoid overhead watering to prevent soil splash.',
291
+ 'Apply copper-based bactericides as a preventative measure, especially before periods of rain.'
292
+ ]
293
+ },
294
+ 'Bell pepper Bell pepper Healthy': {
295
+ 'status': 'Healthy',
296
+ 'description': 'The bell pepper plant is healthy and productive, with strong stems, green leaves, and developing fruit. It shows no signs of the spots, wilting, or stunting associated with common pepper diseases.',
297
+ 'symptoms': [
298
+ 'Leaves are a uniform, deep green color.',
299
+ 'The plant is upright and shows no signs of wilting.',
300
+ 'Flowers and fruit are developing normally without any spots or rot.'
301
+ ],
302
+ 'prevention': [
303
+ 'Provide consistent watering and a balanced fertilizer to avoid stress.',
304
+ 'Stake or cage plants to keep them upright and improve air circulation.',
305
+ 'Monitor for common pests such as aphids, which can transmit viruses.',
306
+ 'Ensure the soil is well-drained to prevent root rot.'
307
+ ]
308
+ },
309
+ 'Potato Potato Early blight': {
310
+ 'status': 'Infected',
311
+ 'description': 'Early Blight is a common fungal disease of potatoes and tomatoes caused by Alternaria solani. It typically appears earlier in the season than Late Blight and is favored by warm, humid weather.',
312
+ 'symptoms': [
313
+ 'Dark brown to black spots on lower, older leaves, often with a characteristic "target" or "bullseye" pattern of concentric rings.',
314
+ 'A yellow halo frequently surrounds the lesions.',
315
+ 'Severe infections cause leaves to yellow and die, reducing yield.',
316
+ 'Tubers can be infected, showing dark, sunken, circular lesions on the surface.'
317
+ ],
318
+ 'prevention': [
319
+ 'Use certified, disease-free seed potatoes.',
320
+ 'Rotate crops with non-susceptible plants for at least two years.',
321
+ 'Maintain plant vigor with adequate nutrition and water, as stressed plants are more susceptible.',
322
+ 'Apply fungicides preventatively, especially when weather conditions are favorable for disease.'
323
+ ]
324
+ },
325
+ 'Potato Potato Late blight': {
326
+ 'status': 'Infected',
327
+ 'description': 'Late Blight, caused by the water mold Phytophthora infestans, is a highly destructive disease of potatoes and tomatoes. It spreads rapidly in cool, moist conditions and can destroy a crop in a short time.',
328
+ 'symptoms': [
329
+ 'Large, dark green to black, water-soaked lesions on leaves and stems, often with a pale green border.',
330
+ 'In high humidity, a fuzzy white ring of mold (spores) may appear on the underside of leaf lesions.',
331
+ 'The disease progresses rapidly, killing foliage and leaving a distinct foul odor.',
332
+ 'Infected tubers develop a reddish-brown, dry rot that extends into the flesh.'
333
+ ],
334
+ 'prevention': [
335
+ 'The most critical step is to use certified, disease-free seed potatoes.',
336
+ 'Destroy all cull piles and volunteer potato/tomato plants before the season starts.',
337
+ 'Promote good air circulation and avoid overhead irrigation later in the day.',
338
+ 'A strict, preventative fungicide spray program is essential for control in most regions.'
339
+ ]
340
+ },
341
+ 'Potato Potato Healthy': {
342
+ 'status': 'Healthy',
343
+ 'description': 'The potato plant is vigorous and healthy, with a full, green canopy. It is currently free from the tell-tale signs of Early Blight, Late Blight, and other common potato ailments.',
344
+ 'symptoms': [
345
+ 'Foliage is uniformly green and upright.',
346
+ 'There are no spots, lesions, or wilting present on the leaves or stems.',
347
+ 'The plant appears to be growing well for its developmental stage.'
348
+ ],
349
+ 'prevention': [
350
+ 'Practice good hilling to cover developing tubers and protect them from light (which turns them green) and pests.',
351
+ 'Scout for and manage insect pests, especially the Colorado potato beetle.',
352
+ 'Ensure consistent soil moisture to prevent issues like scab and hollow heart.',
353
+ 'Begin a preventative fungicide program based on local extension recommendations.'
354
+ ]
355
+ },
356
+ 'Strawberry Strawberry Leaf scorch': {
357
+ 'status': 'Infected',
358
+ 'description': 'Strawberry Leaf Scorch is a fungal disease caused by Diplocarpon earlianum. It creates purplish blotches on leaves and can reduce plant vigor and yield if it becomes severe.',
359
+ 'symptoms': [
360
+ 'Irregular, purplish to reddish-brown blotches on the upper surface of leaves.',
361
+ 'Unlike other leaf spots, these lesions do not have a distinct tan center.',
362
+ 'The spots can merge, causing the tissue between veins to dry up and look "scorched".',
363
+ 'Infected fruit caps (calyxes) can turn brown and die.'
364
+ ],
365
+ 'prevention': [
366
+ 'Plant resistant strawberry varieties.',
367
+ 'After harvest, renovate the strawberry bed by mowing off the old leaves and removing them.',
368
+ 'Use mulch to reduce water splashing and keep leaves dry.',
369
+ 'Ensure good air circulation through proper plant spacing.'
370
+ ]
371
+ },
372
+ 'Strawberry Strawberry Healthy': {
373
+ 'status': 'Healthy',
374
+ 'description': 'The strawberry plant is in excellent health, with vibrant green foliage and no signs of leaf spots, scorch, or fruit rot. The plant is well-positioned for a productive harvest.',
375
+ 'symptoms': [
376
+ 'The leaves are a healthy, uniform green without any purple, tan, or brown spots.',
377
+ 'The crown of the plant is robust and free from rot.',
378
+ 'Flowers and developing berries are clean and show no signs of mold.'
379
+ ],
380
+ 'prevention': [
381
+ 'Plant in raised beds or well-drained soil to prevent root diseases.',
382
+ 'Use a thick layer of straw mulch to keep berries clean and off the soil.',
383
+ 'Water in the morning so foliage has time to dry before evening.',
384
+ 'Rotate the strawberry patch to a new location every 3-4 years.'
385
+ ]
386
+ },
387
+ 'Tomato Tomato Bacterial spot': {
388
+ 'status': 'Infected',
389
+ 'description': 'Bacterial Spot on tomatoes is a common and damaging disease caused by Xanthomonas bacteria. It spreads quickly in warm, wet conditions and can significantly impact both the foliage and the fruit.',
390
+ 'symptoms': [
391
+ 'Small, water-soaked, angular spots on leaves that turn greasy and then black.',
392
+ 'Lesions often have a yellow halo and may cause the leaf to yellow and drop.',
393
+ 'On fruit, spots are slightly raised and scab-like, making the fruit unmarketable.',
394
+ 'Stem infections appear as dark, elongated lesions.'
395
+ ],
396
+ 'prevention': [
397
+ 'Start with certified clean seed and disease-free transplants.',
398
+ 'Rotate crops, ensuring tomatoes or peppers are not planted in the same spot for at least one year.',
399
+ 'Use mulch to act as a barrier between the soil and the plant, reducing water splash.',
400
+ 'Avoid handling or pruning plants when they are wet to prevent spreading the bacteria.'
401
+ ]
402
+ },
403
+ 'Tomato Tomato Early blight': {
404
+ 'status': 'Infected',
405
+ 'description': 'Early Blight, caused by the fungus Alternaria solani, is a very common disease of tomatoes. It typically appears on lower leaves early in the season and progresses up the plant, thriving in humid weather.',
406
+ 'symptoms': [
407
+ 'Dark brown to black spots on lower leaves, often with a "target" or "bullseye" pattern of concentric rings.',
408
+ 'A yellow halo usually surrounds the dark lesions.',
409
+ 'Dark, sunken cankers can form on stems, a condition known as "collar rot" if at the soil line.',
410
+ 'Fruit can be infected near the stem, showing large, sunken, black spots with concentric rings.'
411
+ ],
412
+ 'prevention': [
413
+ 'Plant resistant tomato varieties if available.',
414
+ 'Stake, cage, or trellis plants to improve air circulation and keep foliage off the ground.',
415
+ 'Prune off the bottom 12 inches of foliage once the plant is established.',
416
+ 'Apply a preventative fungicide program, especially as fruit begins to mature.'
417
+ ]
418
+ },
419
+ 'Tomato Tomato Late blight': {
420
+ 'status': 'Infected',
421
+ 'description': 'Late Blight, caused by the oomycete Phytophthora infestans, is the most destructive disease of tomatoes and potatoes. It spreads extremely fast in cool, moist weather and can destroy an entire crop.',
422
+ 'symptoms': [
423
+ 'Large, dark green to black, water-soaked looking lesions on leaves and stems.',
424
+ 'In humid conditions, a downy white ring of mold will appear on the underside of the leaf lesions.',
425
+ 'Stems can develop large black lesions, causing the plant to collapse.',
426
+ 'Fruit develops large, firm, greasy, brown-to-black blotches and rots quickly.'
427
+ ],
428
+ 'prevention': [
429
+ 'Plant late blight-resistant varieties where possible.',
430
+ 'Destroy all volunteer potato and tomato plants, as they can be an early source of infection.',
431
+ 'Space plants far apart to ensure maximum air circulation.',
432
+ 'A strict, preventative fungicide spray schedule is critical for control in susceptible regions.'
433
+ ]
434
+ },
435
+ 'Tomato Tomato Leaf Mold': {
436
+ 'status': 'Infected',
437
+ 'description': 'Leaf Mold, caused by the fungus Passalora fulva, is primarily a disease of tomatoes grown in greenhouses or high-tunnels where humidity is high and air circulation is poor.',
438
+ 'symptoms': [
439
+ 'Pale greenish-yellow spots with indefinite margins on the upper surface of leaves.',
440
+ 'Directly corresponding to these spots on the leaf underside is a dense, olive-green to brownish, velvety mold.',
441
+ 'Infections start on lower leaves and move up the plant.',
442
+ 'Severely infected leaves turn yellow, wither, and drop off.'
443
+ ],
444
+ 'prevention': [
445
+ 'Use tomato varieties resistant to the prevalent races of Leaf Mold.',
446
+ 'Dramatically improve air circulation by using fans and proper plant spacing.',
447
+ 'Keep relative humidity below 85%. Vent greenhouses in the morning.',
448
+ 'Water the soil directly and avoid getting the foliage wet.'
449
+ ]
450
+ },
451
+ 'Tomato Tomato Septoria leaf spot': {
452
+ 'status': 'Infected',
453
+ 'description': 'Septoria Leaf Spot is another very common fungal disease of tomatoes, caused by Septoria lycopersici. It can be distinguished from Early Blight by its numerous small spots.',
454
+ 'symptoms': [
455
+ 'Numerous small, circular spots (about 1/8 inch) appear on the lower leaves.',
456
+ 'The spots have dark brown borders and lighter tan or gray centers.',
457
+ 'Tiny black dots (the fungal pycnidia) can be seen in the center of the spots, a key diagnostic feature.',
458
+ 'The disease progresses up the plant, causing lower leaves to yellow, die, and fall off.'
459
+ ],
460
+ 'prevention': [
461
+ 'Rotate crops and remove plant debris at the end of the season.',
462
+ 'Apply a layer of mulch after planting to prevent spores from splashing up from the soil.',
463
+ 'Water at the base of the plant to keep foliage dry.',
464
+ 'Remove and destroy the lowest infected leaves as soon as they appear.'
465
+ ]
466
+ },
467
+ 'Tomato Tomato Spider mites': {
468
+ 'status': 'Infected',
469
+ 'description': 'The Two-Spotted Spider Mite is a common pest of many plants, including tomatoes. They are not insects but tiny arachnids that feed by piercing leaf cells and sucking out their contents, thriving in hot, dry conditions.',
470
+ 'symptoms': [
471
+ 'Fine, yellow or white stippling (tiny dots) appears on the upper surface of the leaves.',
472
+ 'As the infestation grows, leaves may take on a yellow or bronze appearance.',
473
+ 'In severe cases, fine, delicate webbing can be seen on the underside of leaves and around stems.',
474
+ 'The plant may appear generally weak, and leaves can become dry and fall off.'
475
+ ],
476
+ 'prevention': [
477
+ 'Keep plants well-watered, as mites prefer drought-stressed plants.',
478
+ 'Regularly spray the underside of leaves with a strong jet of water to dislodge mites.',
479
+ 'Introduce or encourage natural predators like predatory mites and ladybugs.',
480
+ 'For heavy infestations, use insecticidal soaps or horticultural oils, ensuring thorough coverage of the undersides of leaves.'
481
+ ]
482
+ },
483
+ 'Tomato Tomato Target Spot': {
484
+ 'status': 'Infected',
485
+ 'description': 'Target Spot is a fungal disease of tomatoes caused by Corynespora cassiicola. It affects all parts of the plant and can be confused with Early Blight, but the spots are typically smaller and more numerous.',
486
+ 'symptoms': [
487
+ 'Small, water-soaked spots on upper leaf surfaces that enlarge into lesions with dark, concentric rings (a "target" look).',
488
+ 'Lesions often have a yellow halo and can cause significant defoliation.',
489
+ 'On fruit, spots are sunken, pitted, and can reach up to a half-inch in diameter.',
490
+ 'Stem lesions are elongated and have the same target-like appearance.'
491
+ ],
492
+ 'prevention': [
493
+ 'Improve air circulation through staking and pruning.',
494
+ 'Avoid overhead watering to keep foliage as dry as possible.',
495
+ 'Rotate crops and manage weeds around the tomato patch.',
496
+ 'A preventative fungicide program can be effective in managing the disease.'
497
+ ]
498
+ },
499
+ 'Tomato Tomato Mosaic virus': {
500
+ 'status': 'Infected',
501
+ 'description': 'Tomato Mosaic Virus (ToMV) is a persistent viral disease that can severely impact tomato plants. It is easily transmitted mechanically by hands, tools, and clothing, and can survive for years in plant debris or on contaminated surfaces.',
502
+ 'symptoms': [
503
+ 'A light green and dark green mosaic or mottled pattern on the leaves.',
504
+ 'Leaves may be malformed, curled, or have a "fernleaf" appearance.',
505
+ 'Plants are often stunted, and fruit yield is significantly reduced.',
506
+ 'Occasionally, fruit may show internal browning or yellow rings.'
507
+ ],
508
+ 'prevention': [
509
+ 'The most effective method is to plant ToMV-resistant tomato varieties.',
510
+ 'Practice strict sanitation. Wash hands with soap and water before handling plants.',
511
+ 'Do not use tobacco products near tomato plants, as the virus can be present in many tobacco products.',
512
+ 'Remove and destroy infected plants immediately to prevent further spread.'
513
+ ]
514
+ },
515
+ 'Tomato Tomato Yellow Leaf Curl Virus': {
516
+ 'status': 'Infected',
517
+ 'description': 'Tomato Yellow Leaf Curl Virus (TYLCV) is a devastating viral disease transmitted primarily by whiteflies. It causes severe stunting and discoloration, dramatically reducing the plant\'s ability to produce fruit.',
518
+ 'symptoms': [
519
+ 'Severe stunting of the plant is a primary symptom; infected plants stop growing.',
520
+ 'New leaves are much smaller than normal and are curled upwards, cupped, and crumpled.',
521
+ 'The margins of the leaves turn a distinct bright yellow.',
522
+ 'The plant may produce very few flowers, and even fewer fruit.'
523
+ ],
524
+ 'prevention': [
525
+ 'Plant TYLCV-resistant varieties, which is the best line of defense.',
526
+ 'Aggressively control whitefly populations using methods like reflective mulch, insecticides, or physical barriers.',
527
+ 'Remove and immediately destroy any infected plants to reduce the virus source.',
528
+ 'Eliminate weeds in and around the garden, as they can host the whiteflies and the virus.'
529
+ ]
530
+ },
531
+ 'Tomato Tomato Healthy': {
532
+ 'status': 'Healthy',
533
+ 'description': 'This tomato plant is healthy and showing no signs of disease. The leaves are the correct color and shape, and the plant structure is sound, indicating it has the resources for good fruit production.',
534
+ 'symptoms': [
535
+ 'Leaves are uniformly green, without spots, mosaic patterns, or curling.',
536
+ 'Stems are strong and free of cankers or lesions.',
537
+ 'The plant is growing at a normal rate and flowers are setting fruit.'
538
+ ],
539
+ 'prevention': [
540
+ 'Continue to provide consistent watering, aiming for deep soil moisture.',
541
+ 'Use a balanced fertilizer appropriate for the fruiting stage.',
542
+ 'Keep the plant staked or caged to maintain good air circulation.',
543
+ 'Continue to scout for the first signs of common pests or diseases.'
544
+ ]
545
+ }
546
+ }
requirements.txt ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Flask
2
+ tensorflow
3
+ numpy
4
+ Pillow
5
+ ollama
6
+ python-dotenv
7
+ opencv-python-headless
8
+ matplotlib
9
+ seaborn
10
+ openai
11
+ bytez
showcase_data.py ADDED
@@ -0,0 +1,484 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # In-depth descriptions for the model showcase, using keys from disease_data.py as the source of truth.
2
+ SHOWCASE_DATA = {
3
+ 'Apple Apple Scab': {
4
+ 'title': 'Apple Scab',
5
+ 'short_description': 'A common fungal disease that affects apple and crabapple trees, causing cosmetic damage to leaves and fruit. While it can reduce yield, it rarely kills the tree, and with proper care, your tree can thrive.',
6
+ 'quick_symptoms': [
7
+ 'Olive-green or brown spots on leaves, fruit, and twigs.',
8
+ 'Leaves may become twisted and puckered.',
9
+ 'Infected fruit develops dark, scabby lesions.'
10
+ ],
11
+ 'fast_prevention': [
12
+ 'Plant resistant varieties and ensure good air circulation through pruning.',
13
+ 'Rake up and destroy fallen leaves in autumn to reduce fungal overwintering.'
14
+ ]
15
+ },
16
+ 'Apple Apple Black rot': {
17
+ 'title': 'Apple Black Rot',
18
+ 'short_description': "A fungal disease that can cause serious damage to apple trees, affecting fruit, leaves, and bark. Early detection and management are key to protecting your harvest and the tree's health.",
19
+ 'quick_symptoms': [
20
+ 'Firm, brown-to-black rot on fruit that expands in concentric rings.',
21
+ 'Purple or brown spots on leaves, often leading to defoliation.',
22
+ 'Sunken, reddish-brown cankers on branches.'
23
+ ],
24
+ 'fast_prevention': [
25
+ 'Prune out dead or cankered wood during dormancy.',
26
+ 'Maintain a regular spray schedule and promote rapid drying of foliage.'
27
+ ]
28
+ },
29
+ 'Apple Cedar apple rust': {
30
+ 'title': 'Cedar Apple Rust',
31
+ 'short_description': 'A fascinating and complex fungal disease requiring both an apple or crabapple and a cedar or juniper tree to complete its lifecycle. It primarily affects leaves and fruit, creating striking but damaging spots.',
32
+ 'quick_symptoms': [
33
+ 'Bright yellow-orange spots on leaves, often with a reddish border.',
34
+ 'Small, raised tube-like structures (aecia) may appear on the underside of leaves.',
35
+ 'Fruit may develop similar orange lesions.'
36
+ ],
37
+ 'fast_prevention': [
38
+ 'Avoid planting susceptible apple varieties near Eastern red cedars.',
39
+ 'Apply fungicides during the primary infection period in spring.'
40
+ ]
41
+ },
42
+ 'Apple Apple Healthy': {
43
+ 'title': 'Healthy Apple Tree',
44
+ 'short_description': "Your apple tree is flourishing, showing vibrant growth and no signs of significant disease. Consistent care will ensure a bountiful harvest and long-term vitality.",
45
+ 'quick_symptoms': [
46
+ 'Lush, green foliage with uniform color.',
47
+ 'Smooth, unblemished bark and strong branch structure.',
48
+ 'Plentiful, well-formed buds and flowers.'
49
+ ],
50
+ 'fast_prevention': [
51
+ 'Continue providing consistent water, sunlight, and proper nutrition.',
52
+ 'Monitor regularly for early signs of pests or disease to act quickly.'
53
+ ]
54
+ },
55
+ 'Cherry Cherry Powdery mildew': {
56
+ 'title': 'Cherry Powdery Mildew',
57
+ 'short_description': 'A fungal disease that covers leaves and fruit with a white, powdery coating. It thrives in moderate temperatures and high humidity, but can be managed effectively with good cultural practices.',
58
+ 'quick_symptoms': [
59
+ 'White, powdery patches on leaves, shoots, and sometimes fruit.',
60
+ 'Leaves can become distorted, curl, or drop prematurely.',
61
+ 'Infected fruit may be stunted or russeted.'
62
+ ],
63
+ 'fast_prevention': [
64
+ 'Prune for good air circulation to reduce humidity within the canopy.',
65
+ 'Apply fungicides or horticultural oils at the first sign of disease.'
66
+ ]
67
+ },
68
+ 'Cherry Cherry Healthy': {
69
+ 'title': 'Healthy Cherry Tree',
70
+ 'short_description': 'A picture of health, this cherry tree shows all the signs of vitality. With continued care, it promises to provide both beauty and a delicious harvest.',
71
+ 'quick_symptoms': [
72
+ 'Bright green, glossy leaves.',
73
+ 'Strong branches with no signs of cankers or oozing.',
74
+ 'Abundant and healthy flower and fruit development.'
75
+ ],
76
+ 'fast_prevention': [
77
+ 'Maintain a consistent watering schedule, especially during dry periods.',
78
+ 'Protect the trunk from mechanical injury and winter sunscald.'
79
+ ]
80
+ },
81
+ 'Corn Corn Gray leaf spot': {
82
+ 'title': 'Corn Gray Leaf Spot',
83
+ 'short_description': 'A significant fungal disease of corn that thrives in warm, humid conditions with extended periods of leaf wetness. It reduces the photosynthetic area of leaves, which can impact grain fill and yield.',
84
+ 'quick_symptoms': [
85
+ 'Small, pale, rectangular lesions that elongate between leaf veins.',
86
+ 'Lesions mature to a grayish or tan color with sharp, defined borders.',
87
+ 'Severe infection can cause leaves to wither and die prematurely.'
88
+ ],
89
+ 'fast_prevention': [
90
+ 'Choose resistant hybrids and practice crop rotation with non-host crops.',
91
+ 'Manage residue through tillage to reduce fungal survival.'
92
+ ]
93
+ },
94
+ 'Corn Corn Common rust': {
95
+ 'title': 'Corn Common Rust',
96
+ 'short_description': 'A fungal disease that appears as small, cinnamon-brown pustules on corn leaves. While widespread, it typically develops later in the season and often has a minimal impact on the yield of healthy plants.',
97
+ 'quick_symptoms': [
98
+ 'Oval or elongated, cinnamon-brown pustules on both upper and lower leaf surfaces.',
99
+ 'Pustules rupture, releasing powdery, rust-colored spores.',
100
+ 'Can cause leaves to yellow and dry prematurely in heavy infections.'
101
+ ],
102
+ 'fast_prevention': [
103
+ 'Planting resistant corn hybrids is the most effective management strategy.',
104
+ 'Most field corn has sufficient resistance, making fungicides unnecessary in many cases.'
105
+ ]
106
+ },
107
+ 'Corn Corn Northern Leaf Blight': {
108
+ 'title': 'Corn Northern Leaf Blight',
109
+ 'short_description': 'A fungal disease characterized by large, cigar-shaped lesions on the leaves of corn plants. It flourishes in cool, wet weather and can cause significant yield loss if it develops early in the season.',
110
+ 'quick_symptoms': [
111
+ 'Long, elliptical, tan or grayish lesions, typically 1 to 6 inches long.',
112
+ 'Lesions first appear on lower leaves and progress up the plant.',
113
+ 'Heavy infestation gives the plant a blighted or "fired" appearance.'
114
+ ],
115
+ 'fast_prevention': [
116
+ 'Select resistant hybrids adapted to your region.',
117
+ 'Use crop rotation and tillage to bury infected residue.'
118
+ ]
119
+ },
120
+ 'Corn Corn Healthy': {
121
+ 'title': 'Healthy Corn Plant',
122
+ 'short_description': 'This corn plant is growing strong and tall, a testament to good soil, adequate sun, and proper care. It is on the right track for producing high-quality ears.',
123
+ 'quick_symptoms': [
124
+ 'Tall, sturdy stalks with broad, deep green leaves.',
125
+ 'Well-developed tassels and silks appearing at the appropriate time.',
126
+ 'No signs of spots, streaks, or stunting.'
127
+ ],
128
+ 'fast_prevention': [
129
+ 'Ensure consistent soil moisture, providing about 1 inch of water per week.',
130
+ 'Manage weeds to reduce competition for nutrients and water.'
131
+ ]
132
+ },
133
+ 'Grape Grape Black rot': {
134
+ 'title': 'Grape Black Rot',
135
+ 'short_description': 'A serious fungal disease of grapes that can destroy the entire fruit crop if left unchecked. It affects all green parts of the vine, but the damage to the fruit is the most devastating.',
136
+ 'quick_symptoms': [
137
+ 'Small, brownish, circular spots on leaves that develop dark borders.',
138
+ 'Fruit rots, turns dark brown, and then shrivels into hard, black "mummies".',
139
+ 'Dark, elongated lesions can appear on shoots and tendrils.'
140
+ ],
141
+ 'fast_prevention': [
142
+ 'Choose a sunny, well-drained site with good air movement.',
143
+ 'Apply fungicides from early spring through the growing season.'
144
+ ]
145
+ },
146
+ 'Grape Grape Black Measles': {
147
+ 'title': 'Grape Esca (Black Measles)',
148
+ 'short_description': 'A complex and destructive trunk disease caused by a group of fungi. It can lead to a gradual decline or sudden dieback of the vine, with symptoms that can be quite dramatic.',
149
+ 'quick_symptoms': [
150
+ 'Leaves show "tiger stripe" patterns of yellowing or reddening between veins.',
151
+ 'Small, dark spots or "measles" may appear on grape berries.',
152
+ 'In acute cases, the entire vine or a part of it may suddenly wilt and die.'
153
+ ],
154
+ 'fast_prevention': [
155
+ 'Practice good pruning hygiene, making clean cuts and protecting large wounds.',
156
+ 'Delay pruning until late winter or early spring to allow for wound healing.'
157
+ ]
158
+ },
159
+ 'Grape Grape Isariopsis Leaf Spot': {
160
+ 'title': 'Grape Leaf Blight',
161
+ 'short_description': 'Also known as Isariopsis Leaf Spot, this fungal disease typically appears late in the season. While it can cause some defoliation, it is generally considered a minor disease and rarely impacts yield significantly.',
162
+ 'quick_symptoms': [
163
+ 'Irregular, dark brown or black lesions on leaves, often surrounded by a yellow halo.',
164
+ 'Lesions can merge, causing large areas of the leaf to die.',
165
+ 'Infected leaves may drop prematurely.'
166
+ ],
167
+ 'fast_prevention': [
168
+ 'Rake and destroy fallen leaves to reduce the source of infection.',
169
+ 'Promote good air circulation through proper pruning and training of vines.'
170
+ ]
171
+ },
172
+ 'Grape Grape Healthy': {
173
+ 'title': 'Healthy Grape Vine',
174
+ 'short_description': 'This grapevine is vigorous and well-structured, a sign of excellent health and potential. It is perfectly poised to produce a crop of delicious, high-quality grapes.',
175
+ 'quick_symptoms': [
176
+ 'Lush canopy of large, uniformly green leaves.',
177
+ 'Strong, flexible canes and a sturdy trunk.',
178
+ 'Healthy development of flower clusters and fruit set.'
179
+ ],
180
+ 'fast_prevention': [
181
+ 'Maintain a balanced fertilization program based on soil tests.',
182
+ 'Ensure the trellis system provides adequate support and sun exposure.'
183
+ ]
184
+ },
185
+ 'Peach Peach Bacterial spot': {
186
+ 'title': 'Peach Bacterial Spot',
187
+ 'short_description': "A persistent bacterial disease that affects peaches, nectarines, and plums, causing spots on leaves and fruit. It is most severe in warm, rainy weather and on sandy soils.",
188
+ 'quick_symptoms': [
189
+ 'Angular, purple or black spots on leaves, with centers that may fall out.',
190
+ 'Leaves can turn yellow and drop, leading to significant defoliation.',
191
+ 'Fruit develops pitted, cracked, or sunken spots.'
192
+ ],
193
+ 'fast_prevention': [
194
+ 'Plant resistant varieties when available.',
195
+ 'Maintain tree vigor with proper fertilization and irrigation to help withstand infection.'
196
+ ]
197
+ },
198
+ 'Peach Peach Healthy': {
199
+ 'title': 'Healthy Peach Tree',
200
+ 'short_description': "Your peach tree is thriving, showing the robust health needed for a sweet and juicy harvest. It's a beautiful example of a well-cared-for fruit tree.",
201
+ 'quick_symptoms': [
202
+ 'Full canopy of vibrant, green leaves.',
203
+ 'Smooth bark without cankers or signs of insect borers.',
204
+ 'Strong fruit development with good color and size.'
205
+ ],
206
+ 'fast_prevention': [
207
+ 'Thin fruit to encourage larger peaches and prevent branch breakage.',
208
+ 'Apply dormant oil in late winter to control overwintering pests.'
209
+ ]
210
+ },
211
+ 'Bell pepper Bell pepper Bacterial spot': {
212
+ 'title': 'Bell Pepper Bacterial Spot',
213
+ 'short_description': 'A common bacterial infection in warm, humid climates that can cause spotting on leaves and fruit, potentially impacting yield and quality. Good sanitation is your first line of defense.',
214
+ 'quick_symptoms': [
215
+ 'Small, water-soaked spots on leaves that turn dark and angular.',
216
+ 'Spots may have a yellow halo and centers can fall out, creating a "shot-hole" look.',
217
+ 'Fruit develops raised, scab-like spots.'
218
+ ],
219
+ 'fast_prevention': [
220
+ 'Use disease-free seeds and rotate crops, avoiding planting peppers in the same spot for at least a year.',
221
+ 'Water at the base of the plant to keep foliage dry.'
222
+ ]
223
+ },
224
+ 'Bell pepper Bell pepper Healthy': {
225
+ 'title': 'Healthy Bell Pepper Plant',
226
+ 'short_description': 'This bell pepper plant is thriving, exhibiting strong stems and vibrant leaves, ready to produce delicious fruit. Your attentive care is paying off beautifully.',
227
+ 'quick_symptoms': [
228
+ 'Sturdy stems and full, deep green leaves.',
229
+ 'Absence of spots, yellowing, or wilting.',
230
+ 'Vigorous production of flowers and young peppers.'
231
+ ],
232
+ 'fast_prevention': [
233
+ 'Ensure consistent watering and well-drained soil.',
234
+ 'Provide support as the plant grows to prevent stem breakage.'
235
+ ]
236
+ },
237
+ 'Potato Potato Early blight': {
238
+ 'title': 'Potato Early Blight',
239
+ 'short_description': 'A common fungal disease of potatoes and tomatoes, caused by Alternaria species. Despite its name, it often appears after the plants have blossomed, causing distinctive "target-like" spots on leaves.',
240
+ 'quick_symptoms': [
241
+ 'Dark brown to black spots on lower leaves, often with a "bulls-eye" pattern of concentric rings.',
242
+ 'A yellow halo may surround the spots.',
243
+ 'Tubers can develop dark, sunken lesions.'
244
+ ],
245
+ 'fast_prevention': [
246
+ 'Rotate crops, avoiding potatoes or tomatoes in the same plot for 2-3 years.',
247
+ 'Ensure good nutrition and soil moisture to keep plants vigorous.'
248
+ ]
249
+ },
250
+ 'Potato Potato Late blight': {
251
+ 'title': 'Potato Late Blight',
252
+ 'short_description': 'A devastating disease of potatoes and tomatoes caused by a water mold, Phytophthora infestans. It thrives in cool, moist conditions and can destroy a crop in a very short time.',
253
+ 'quick_symptoms': [
254
+ 'Large, dark green to black, water-soaked lesions on leaves and stems.',
255
+ 'A fuzzy, white mold may appear on the underside of leaves in humid conditions.',
256
+ 'Tubers develop a reddish-brown, firm rot under the skin.'
257
+ ],
258
+ 'fast_prevention': [
259
+ 'Use certified disease-free seed potatoes.',
260
+ 'Apply fungicides preventively, especially when cool, wet weather is forecast.'
261
+ ]
262
+ },
263
+ 'Potato Potato Healthy': {
264
+ 'title': 'Healthy Potato Plant',
265
+ 'short_description': 'This potato plant is the picture of health, with lush foliage and strong stems. It is working hard below the ground to produce a fantastic crop of tubers.',
266
+ 'quick_symptoms': [
267
+ 'Dense, upright canopy of green leaves.',
268
+ 'No signs of yellowing, spotting, or wilting.',
269
+ 'Vigorous growth throughout the season.'
270
+ ],
271
+ 'fast_prevention': [
272
+ '"Hill" soil around the base of the plants to protect developing tubers from sun and pests.',
273
+ 'Monitor for pests like the Colorado potato beetle.'
274
+ ]
275
+ },
276
+ 'Strawberry Strawberry Leaf scorch': {
277
+ 'title': 'Strawberry Leaf Scorch',
278
+ 'short_description': 'A fungal disease that causes purplish-brown blotches on strawberry leaves. While it can make a patch look ragged, it typically has a minor effect on yield unless the infection is very severe.',
279
+ 'quick_symptoms': [
280
+ 'Irregular, purplish to brownish spots on leaves.',
281
+ 'As spots merge, the leaf takes on a "scorched" appearance between the veins.',
282
+ 'Small, dark, speck-like fruiting bodies may appear in the center of lesions.'
283
+ ],
284
+ 'fast_prevention': [
285
+ 'Plant in a sunny location with good air circulation.',
286
+ 'Remove and destroy infected leaves after harvest to reduce inoculum.'
287
+ ]
288
+ },
289
+ 'Strawberry Strawberry Healthy': {
290
+ 'title': 'Healthy Strawberry Plant',
291
+ 'short_description': 'This strawberry plant is vibrant and robust, ready to produce sweet, juicy berries. Its healthy state is a great sign for the delicious treats to come.',
292
+ 'quick_symptoms': [
293
+ 'Bright green, trifoliate leaves with no discoloration.',
294
+ 'A well-developed crown with new growth emerging from the center.',
295
+ 'Plentiful flowers and developing fruit.'
296
+ ],
297
+ 'fast_prevention': [
298
+ 'Apply a layer of straw mulch to keep fruit clean and conserve moisture.',
299
+ 'Renovate the strawberry patch after harvest to maintain vigor.'
300
+ ]
301
+ },
302
+ 'Tomato Tomato Bacterial spot': {
303
+ 'title': 'Tomato Bacterial Spot',
304
+ 'short_description': 'A common and problematic disease of tomatoes, especially in warm, wet conditions. It can defoliate plants and cause unsightly lesions on fruit, making sanitation crucial.',
305
+ 'quick_symptoms': [
306
+ 'Small, water-soaked, angular spots on leaves that later turn greasy and black.',
307
+ 'Spots may have a yellow halo.',
308
+ 'Fruit can have raised, dark, scabby spots.'
309
+ ],
310
+ 'fast_prevention': [
311
+ 'Use disease-free seed and sanitize stakes and tools.',
312
+ 'Avoid overhead watering to keep leaves as dry as possible.'
313
+ ]
314
+ },
315
+ 'Tomato Tomato Early blight': {
316
+ 'title': 'Tomato Early Blight',
317
+ 'short_description': 'A very common fungal disease that starts on the lower, older leaves and works its way up the plant. It can cause significant defoliation, which exposes fruit to sunscald and reduces plant productivity.',
318
+ 'quick_symptoms': [
319
+ 'Dark, concentric "bulls-eye" spots on lower leaves.',
320
+ 'Stems can develop dark, sunken lesions, a condition known as "collar rot".',
321
+ 'Affected leaves yellow and drop.'
322
+ ],
323
+ 'fast_prevention': [
324
+ 'Mulch heavily to prevent spores from splashing from the soil onto leaves.',
325
+ 'Prune lower leaves as the plant grows to improve air circulation.'
326
+ ]
327
+ },
328
+ 'Tomato Tomato Late blight': {
329
+ 'title': 'Tomato Late Blight',
330
+ 'short_description': 'The same aggressive water mold that affects potatoes, late blight can decimate a tomato patch in just a few days under cool, wet conditions. Vigilance is absolutely key.',
331
+ 'quick_symptoms': [
332
+ 'Large, greasy, gray-green to blackish blotches on leaves.',
333
+ 'White, fuzzy mold can grow on leaf undersides.',
334
+ 'Fruit develops large, firm, brown or olive-colored greasy spots.'
335
+ ],
336
+ 'fast_prevention': [
337
+ 'Monitor weather forecasts and apply preventative fungicides when late blight is reported in your area.',
338
+ 'Ensure ample spacing between plants for quick drying.'
339
+ ]
340
+ },
341
+ 'Tomato Tomato Leaf Mold': {
342
+ 'title': 'Tomato Leaf Mold',
343
+ 'short_description': 'A fungal disease that thrives in high humidity, making it a common problem in greenhouses but also possible in the field. It primarily affects the leaves, reducing the plant\u0027s ability to photosynthesize.',
344
+ 'quick_symptoms': [
345
+ 'Pale green or yellowish spots on the upper side of leaves.',
346
+ 'An olive-green to brownish, velvety mold grows on the underside of these spots.',
347
+ 'Infected leaves eventually wither and die.'
348
+ ],
349
+ 'fast_prevention': [
350
+ 'Provide excellent air circulation and stake or trellis plants.',
351
+ 'Water the soil directly and avoid getting the foliage wet, especially late in the day.'
352
+ ]
353
+ },
354
+ 'Tomato Tomato Septoria leaf spot': {
355
+ 'title': 'Tomato Septoria Leaf Spot',
356
+ 'short_description': 'Another prolific leaf-spotting disease of tomato that starts on the bottom of the plant and moves up. It can cause extensive defoliation, weakening the plant and impacting fruit production.',
357
+ 'quick_symptoms': [
358
+ 'Many small, circular spots with dark borders and tan or gray centers.',
359
+ 'Tiny black dots (pycnidia) can be seen in the center of the spots.',
360
+ 'Affected leaves turn yellow, then brown, and fall off.'
361
+ ],
362
+ 'fast_prevention': [
363
+ 'Rotate crops and control weeds, especially those in the nightshade family.',
364
+ 'Remove infected lower leaves as they appear to slow the disease\u0027s spread.'
365
+ ]
366
+ },
367
+ 'Tomato Tomato Spider mites': {
368
+ 'title': 'Two-Spotted Spider Mite',
369
+ 'short_description': 'These tiny arachnids are not insects but are common pests on many plants, including tomatoes. They use piercing mouthparts to suck cell contents from leaves, causing a stippling effect.',
370
+ 'quick_symptoms': [
371
+ 'Yellow or white pinprick spots (stippling) on leaves.',
372
+ 'Fine webbing may be visible on the underside of leaves and around stems.',
373
+ 'In heavy infestations, leaves can turn yellow or bronze and drop off.'
374
+ ],
375
+ 'fast_prevention': [
376
+ 'Regularly spray plants with a strong stream of water to dislodge mites.',
377
+ 'Encourage predatory insects or use insecticidal soaps or horticultural oils.'
378
+ ]
379
+ },
380
+ 'Tomato Tomato Target Spot': {
381
+ 'title': 'Tomato Target Spot',
382
+ 'short_description': 'Caused by the fungus Corynespora cassiicola, this disease affects leaves, stems, and fruit. Its "target spot" lesions can be confused with Early Blight, but they have their own distinct characteristics.',
383
+ 'quick_symptoms': [
384
+ 'Brown to black leaf spots with distinct concentric rings, creating a "target" look.',
385
+ 'Spots are often larger than Septoria and may not have the dark specks.',
386
+ 'Fruit can develop sunken, pitted spots, often near the stem end.'
387
+ ],
388
+ 'fast_prevention': [
389
+ 'Improve air circulation by staking and pruning plants.',
390
+ 'Rotate with non-susceptible crops and maintain a mulch layer.'
391
+ ]
392
+ },
393
+ 'Tomato Tomato Mosaic virus': {
394
+ 'title': 'Tomato Mosaic Virus',
395
+ 'short_description': 'A persistent viral disease that can survive for years in plant debris or on contaminated tools. It causes a mosaic-like pattern on leaves and can reduce both fruit quality and yield.',
396
+ 'quick_symptoms': [
397
+ 'Light and dark green mottling or mosaic pattern on leaves.',
398
+ 'Leaves may be fern-like in appearance or otherwise distorted.',
399
+ 'Internal browning can occur in the fruit.'
400
+ ],
401
+ 'fast_prevention': [
402
+ 'Wash hands thoroughly, especially if you use tobacco products, before handling plants.',
403
+ 'Disinfect tools and avoid working with plants when they are wet.'
404
+ ]
405
+ },
406
+ 'Tomato Tomato Yellow Leaf Curl Virus': {
407
+ 'title': 'Tomato Yellow Leaf Curl Virus',
408
+ 'short_description': 'A devastating viral disease spread by whiteflies. It severely stunts the plant and can lead to a total loss of fruit production. Management focuses entirely on controlling the insect vector.',
409
+ 'quick_symptoms': [
410
+ 'New leaves are small, cupped upward, and have yellow margins.',
411
+ 'The plant becomes severely stunted and bushy due to shortened internodes.',
412
+ 'Flower and fruit production is drastically reduced or ceases altogether.'
413
+ ],
414
+ 'fast_prevention': [
415
+ 'Control whitefly populations using reflective mulches, insecticides, or physical barriers like netting.',
416
+ 'Remove and destroy infected plants immediately to reduce the virus source.'
417
+ ]
418
+ },
419
+ 'Tomato Tomato Healthy': {
420
+ 'title': 'Healthy Tomato Plant',
421
+ 'short_description': 'This tomato plant is a model of health, with lush foliage and a strong structure. It is well on its way to producing a delicious and rewarding crop.',
422
+ 'quick_symptoms': [
423
+ 'Deep green leaves and sturdy, upright stems.',
424
+ 'Vibrant yellow flowers and steadily developing fruit.',
425
+ 'No evidence of spots, wilting, or pest damage.'
426
+ ],
427
+ 'fast_prevention': [
428
+ 'Provide consistent water and support with stakes or cages.',
429
+ 'Feed regularly with a balanced fertilizer designed for tomatoes.'
430
+ ]
431
+ },
432
+ 'Citrus Citrus Black spot': {
433
+ 'title': 'Citrus Black Spot',
434
+ 'short_description': 'A fungal disease primarily affecting the rind of citrus fruits, causing cosmetic blemishes that can make the fruit unmarketable. It is a major concern in citrus-growing regions with summer rainfall.',
435
+ 'quick_symptoms': [
436
+ 'Small, hard, black, circular spots on the fruit rind.',
437
+ 'Lesions can vary in appearance, including virulent spots, false melanose, and freckle spots.',
438
+ 'Does not typically cause leaf symptoms or tree decline.'
439
+ ],
440
+ 'fast_prevention': [
441
+ 'Apply timely and thorough fungicide sprays to protect the developing fruit.',
442
+ 'Manage leaf litter on the orchard floor, as it is a source of fungal spores.'
443
+ ]
444
+ },
445
+ 'Citrus Citrus canker': {
446
+ 'title': 'Citrus Canker',
447
+ 'short_description': 'A highly contagious bacterial disease that causes lesions on citrus leaves, stems, and fruit. Severe infections can lead to defoliation, blemished fruit, and premature fruit drop.',
448
+ 'quick_symptoms': [
449
+ 'Raised, corky or spongy lesions on leaves, often surrounded by a water-soaked margin and a yellow halo.',
450
+ 'Lesions are visible on both sides of the leaf.',
451
+ 'Fruit lesions can become scab-like and may ooze bacteria.'
452
+ ],
453
+ 'fast_prevention': [
454
+ 'Strict quarantine measures and eradication of infected trees are the primary controls.',
455
+ 'Planting windbreaks to reduce the spread of bacteria by wind-driven rain.'
456
+ ]
457
+ },
458
+ 'Citrus Citrus greening': {
459
+ 'title': 'Citrus Greening (HLB)',
460
+ 'short_description': 'Also known as Huanglongbing (HLB), this is one of the most destructive diseases of citrus worldwide. It is caused by a bacterium that is spread by the Asian citrus psyllid insect.',
461
+ 'quick_symptoms': [
462
+ 'Blotchy, asymmetrical yellowing of leaves (mottling) is the most characteristic symptom.',
463
+ 'Affected trees show stunted growth, sparse yellow foliage, and extensive twig dieback.',
464
+ 'Fruit is often small, lopsided, poorly colored, and has a bitter taste.'
465
+ ],
466
+ 'fast_prevention': [
467
+ 'Aggressive and coordinated control of the psyllid insect vector.',
468
+ 'Prompt removal of infected trees to prevent spread to healthy ones.'
469
+ ]
470
+ },
471
+ 'Citrus Citrus Healthy': {
472
+ 'title': 'Healthy Citrus Tree',
473
+ 'short_description': 'This citrus tree is in excellent health, with lush, deep green leaves and no indication of canker, greening, or other diseases. It is well-positioned for healthy fruit production.',
474
+ 'quick_symptoms': [
475
+ 'Leaves are a uniform, dark green color without yellowing or mottling.',
476
+ 'The canopy is dense and shows vigorous new growth.',
477
+ 'Fruit is developing with good size and color for the season.'
478
+ ],
479
+ 'fast_prevention': [
480
+ 'Monitor for pests, especially the Asian citrus psyllid, and manage them proactively.',
481
+ 'Provide a balanced fertilizer program tailored for citrus trees.'
482
+ ]
483
+ }
484
+ }
static/css/style.css ADDED
@@ -0,0 +1,879 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=Poppins:wght@300;400;600;700;800&display=swap');
2
+
3
+ :root {
4
+ /* Dark Green Theme Colors */
5
+ --primary-green: #22c55e;
6
+ --dark-green: #14532d;
7
+ --darker-green: #052e16;
8
+ --green-glow: rgba(34, 197, 94, 0.3);
9
+ --deep-green: #0d2818;
10
+ --emerald-green: #10b981;
11
+ --forest-green: #166534;
12
+ --lime-green: #65a30d;
13
+
14
+ /* Neutral Colors */
15
+ --primary-color: #ffffff;
16
+ --secondary-color: #d1fae5;
17
+ --text-light: rgba(255, 255, 255, 0.9);
18
+ --text-muted: rgba(255, 255, 255, 0.6);
19
+
20
+ /* Glassmorphism */
21
+ --glass-bg: rgba(255, 255, 255, 0.1);
22
+ --glass-bg-strong: rgba(255, 255, 255, 0.15);
23
+ --glass-border: rgba(255, 255, 255, 0.2);
24
+ --glass-border-hover: rgba(34, 197, 94, 0.4);
25
+ --glass-shadow: rgba(0, 0, 0, 0.5);
26
+ --hover-bg: rgba(255, 255, 255, 0.2);
27
+
28
+ /* Layout */
29
+ --font-family: 'Inter', 'Poppins', sans-serif;
30
+ --sidebar-width: 280px;
31
+ --sidebar-width-collapsed: 85px;
32
+ --content-padding: 2.5rem;
33
+ --border-radius: 20px;
34
+ --border-radius-sm: 12px;
35
+ }
36
+
37
+ * {
38
+ box-sizing: border-box;
39
+ }
40
+
41
+ body, html {
42
+ height: 100%;
43
+ margin: 0;
44
+ padding: 0;
45
+ font-family: var(--font-family);
46
+ color: var(--primary-color);
47
+ overflow: hidden;
48
+ -webkit-font-smoothing: antialiased;
49
+ -moz-osx-font-smoothing: grayscale;
50
+ }
51
+
52
+ /* Plant Leaves Background */
53
+ .background-animation {
54
+ position: fixed;
55
+ top: 0;
56
+ left: 0;
57
+ width: 100%;
58
+ height: 100%;
59
+ z-index: -1;
60
+ background-image:
61
+ linear-gradient(135deg, rgba(5, 46, 22, 0.8) 0%, rgba(13, 40, 24, 0.7) 100%),
62
+ url('../images/green-leaves-3840x2160-23231.jpg');
63
+ background-size: cover;
64
+ background-position: center center;
65
+ background-repeat: no-repeat;
66
+ background-attachment: fixed;
67
+ animation: subtleMove 60s ease-in-out infinite;
68
+
69
+ }
70
+
71
+ .background-animation::before {
72
+ content: '';
73
+ position: absolute;
74
+ top: 0;
75
+ left: 0;
76
+ width: 100%;
77
+ height: 100%;
78
+ background-image:
79
+ repeating-linear-gradient(45deg, transparent, transparent 2px, rgba(34, 197, 94, 0.02) 2px, rgba(34, 197, 94, 0.02) 4px),
80
+ repeating-linear-gradient(-45deg, transparent, transparent 2px, rgba(16, 185, 129, 0.02) 2px, rgba(16, 185, 129, 0.02) 4px);
81
+ pointer-events: none;
82
+ opacity: 0.5;
83
+ }
84
+
85
+ .background-animation::after {
86
+ content: '';
87
+ position: absolute;
88
+ top: 0;
89
+ left: 0;
90
+ width: 100%;
91
+ height: 100%;
92
+ background:
93
+ radial-gradient(circle at 20% 50%, rgba(34, 197, 94, 0.05) 0%, transparent 50%),
94
+ radial-gradient(circle at 80% 80%, rgba(16, 185, 129, 0.05) 0%, transparent 50%),
95
+ radial-gradient(ellipse at 50% 0%, rgba(5, 46, 22, 0.3) 0%, transparent 70%);
96
+ pointer-events: none;
97
+ }
98
+
99
+ /* If user adds plant-leaves-bg.jpg, uncomment this and comment above */
100
+ /*
101
+ .background-animation {
102
+ position: fixed;
103
+ top: 0;
104
+ left: 0;
105
+ width: 100%;
106
+ height: 100%;
107
+ z-index: -1;
108
+ background-image:
109
+ linear-gradient(135deg, rgba(5, 46, 22, 0.85) 0%, rgba(20, 83, 45, 0.75) 50%, rgba(13, 40, 24, 0.85) 100%),
110
+ url('../images/plant-leaves-bg.jpg');
111
+ background-color: rgba(5, 46, 22, 0.95);
112
+ background-size: cover;
113
+ background-position: center center;
114
+ background-repeat: no-repeat;
115
+ background-attachment: fixed;
116
+ filter: brightness(0.35) saturate(1.3) contrast(1.1);
117
+ animation: subtleMove 60s ease-in-out infinite;
118
+ }
119
+ */
120
+
121
+ @keyframes subtleMove {
122
+ 0%, 100% { background-position: center center, 0 0; }
123
+ 50% { background-position: center 2%, 20px 20px; }
124
+ }
125
+
126
+ /* Main Container */
127
+ .main-container {
128
+ display: flex;
129
+ height: 100vh;
130
+ width: 100vw;
131
+ position: relative;
132
+ z-index: 1;
133
+ }
134
+
135
+ #content {
136
+ width: calc(100% - var(--sidebar-width));
137
+ padding: var(--content-padding);
138
+ transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
139
+ overflow-y: auto;
140
+ overflow-x: hidden;
141
+ height: 100vh;
142
+ position: relative;
143
+ }
144
+
145
+ #content.full-width {
146
+ width: calc(100% - var(--sidebar-width-collapsed));
147
+ }
148
+
149
+ /* Enhanced Glassmorphism */
150
+ .glass-card,
151
+ .sidebar-nav,
152
+ .upload-area-glass,
153
+ .chat-box-glass,
154
+ .chat-input-glass {
155
+ background: var(--glass-bg);
156
+ backdrop-filter: blur(25px) saturate(180%);
157
+ -webkit-backdrop-filter: blur(25px) saturate(180%);
158
+ border: 1.5px solid var(--glass-border);
159
+ border-radius: var(--border-radius);
160
+ box-shadow:
161
+ 0 8px 32px var(--glass-shadow),
162
+ 0 2px 8px rgba(0, 0, 0, 0.3),
163
+ inset 0 1px 1px rgba(255, 255, 255, 0.1);
164
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
165
+ }
166
+
167
+ .glass-card:hover,
168
+ .upload-area-glass:hover {
169
+ border-color: var(--glass-border-hover);
170
+ box-shadow:
171
+ 0 12px 48px var(--glass-shadow),
172
+ 0 4px 16px rgba(34, 197, 94, 0.2),
173
+ inset 0 1px 1px rgba(255, 255, 255, 0.15);
174
+ transform: translateY(-2px);
175
+ }
176
+
177
+ .card-header-glass {
178
+ background: rgba(255, 255, 255, 0.05);
179
+ border-bottom: 1.5px solid var(--glass-border);
180
+ padding: 1.25rem 2rem;
181
+ border-top-left-radius: var(--border-radius);
182
+ border-top-right-radius: var(--border-radius);
183
+ font-weight: 600;
184
+ color: var(--primary-color);
185
+ font-size: 1.1rem;
186
+ letter-spacing: 0.5px;
187
+ }
188
+
189
+ .card-body-glass {
190
+ padding: 2rem;
191
+ }
192
+
193
+ /* Sidebar - Enhanced */
194
+ .sidebar-nav {
195
+ min-width: var(--sidebar-width);
196
+ max-width: var(--sidebar-width);
197
+ transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
198
+ display: flex;
199
+ flex-direction: column;
200
+ overflow-y: auto;
201
+ overflow-x: hidden;
202
+ max-height: 100vh;
203
+ background: rgba(0, 0, 0, 0.3);
204
+ backdrop-filter: blur(25px) saturate(180%);
205
+ -webkit-backdrop-filter: blur(25px) saturate(180%);
206
+ border-right: 2px solid var(--glass-border);
207
+ box-shadow: 4px 0 24px rgba(0, 0, 0, 0.4);
208
+ }
209
+
210
+ .sidebar-nav .sidebar-header {
211
+ padding: 2rem 1.5rem;
212
+ text-align: center;
213
+ border-bottom: 2px solid var(--glass-border);
214
+ background: linear-gradient(135deg, rgba(34, 197, 94, 0.1) 0%, transparent 100%);
215
+ }
216
+
217
+ .sidebar-nav .sidebar-header h3 {
218
+ color: var(--primary-green);
219
+ font-weight: 700;
220
+ font-size: 1.75rem;
221
+ margin: 0;
222
+ text-shadow: 0 0 20px var(--green-glow);
223
+ letter-spacing: 1px;
224
+ }
225
+
226
+ .sidebar-nav .sidebar-header h3 i {
227
+ color: var(--emerald-green);
228
+ margin-right: 0.5rem;
229
+ }
230
+
231
+ .sidebar-nav ul.components {
232
+ padding: 1.5rem 0;
233
+ flex-grow: 1;
234
+ margin: 0;
235
+ list-style: none;
236
+ }
237
+
238
+ .sidebar-nav ul li {
239
+ margin: 0.5rem 0;
240
+ padding: 0 0.5rem;
241
+ }
242
+
243
+ .sidebar-nav ul li a {
244
+ padding: 1rem 1.25rem;
245
+ font-size: 1rem;
246
+ font-weight: 500;
247
+ display: flex;
248
+ align-items: center;
249
+ color: var(--text-light);
250
+ text-decoration: none;
251
+ border-left: 3px solid transparent;
252
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
253
+ /* margin: 0 1rem; -- removed */
254
+ border-radius: var(--border-radius-sm);
255
+ position: relative;
256
+ overflow: hidden;
257
+ min-width: 0;
258
+ white-space: nowrap;
259
+ }
260
+
261
+ .sidebar-nav ul li a::before {
262
+ content: '';
263
+ position: absolute;
264
+ top: 0;
265
+ left: -100%;
266
+ width: 100%;
267
+ height: 100%;
268
+ background: linear-gradient(90deg, transparent, rgba(34, 197, 94, 0.2), transparent);
269
+ transition: left 0.5s;
270
+ }
271
+
272
+ .sidebar-nav ul li a:hover::before {
273
+ left: 100%;
274
+ }
275
+
276
+ .sidebar-nav ul li a span {
277
+ text-overflow: ellipsis;
278
+ overflow: hidden;
279
+ white-space: nowrap;
280
+ flex-grow: 1; /* Allow the span to take available space */
281
+ }
282
+
283
+ .sidebar-nav ul li a i {
284
+ margin-right: 1rem;
285
+ width: 24px;
286
+ text-align: center;
287
+ font-size: 1.2rem;
288
+ color: var(--emerald-green);
289
+ transition: all 0.3s;
290
+ }
291
+
292
+ .sidebar-nav ul li a:hover {
293
+ color: var(--primary-color);
294
+ background: var(--hover-bg);
295
+ border-left-color: var(--primary-green);
296
+ transform: translateX(5px);
297
+ box-shadow: 0 4px 12px rgba(34, 197, 94, 0.2);
298
+ }
299
+
300
+ .sidebar-nav ul li.active > a {
301
+ color: var(--primary-color);
302
+ background: linear-gradient(90deg, rgba(34, 197, 94, 0.2), rgba(34, 197, 94, 0.1));
303
+ border-left-color: var(--primary-green);
304
+ box-shadow:
305
+ inset 0 0 20px rgba(34, 197, 94, 0.15),
306
+ 0 4px 12px rgba(34, 197, 94, 0.25);
307
+ font-weight: 600;
308
+ }
309
+
310
+ .sidebar-nav ul li.active > a i {
311
+ color: var(--primary-green);
312
+ text-shadow: 0 0 10px var(--green-glow);
313
+ }
314
+
315
+ .sidebar-toggler {
316
+ padding: 1.5rem;
317
+ text-align: center;
318
+ border-top: 2px solid var(--glass-border);
319
+ background: rgba(5, 46, 22, 0.3);
320
+ }
321
+
322
+ .btn-glass {
323
+ background: var(--glass-bg);
324
+ border: 1.5px solid var(--glass-border);
325
+ color: var(--text-light);
326
+ border-radius: var(--border-radius-sm);
327
+ padding: 0.75rem 1.5rem;
328
+ font-weight: 500;
329
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
330
+ cursor: pointer;
331
+ }
332
+
333
+ .btn-glass:hover {
334
+ background: var(--hover-bg);
335
+ color: var(--primary-color);
336
+ border-color: var(--primary-green);
337
+ box-shadow: 0 4px 16px rgba(34, 197, 94, 0.3);
338
+ transform: translateY(-2px);
339
+ }
340
+
341
+ /* Collapsed Sidebar */
342
+ #sidebar.collapsed {
343
+ min-width: var(--sidebar-width-collapsed);
344
+ max-width: var(--sidebar-width-collapsed);
345
+ }
346
+
347
+ #sidebar.collapsed .sidebar-header h3,
348
+ #sidebar.collapsed ul li a span {
349
+ display: none;
350
+ }
351
+
352
+ #sidebar.collapsed ul li a i {
353
+ margin-right: 0;
354
+ font-size: 1.5rem;
355
+ }
356
+
357
+ #sidebar.collapsed ul li a {
358
+ justify-content: center;
359
+ padding: 1rem;
360
+ margin: 0.5rem;
361
+ }
362
+
363
+ #sidebar.collapsed .sidebar-toggler i {
364
+ transform: rotate(180deg);
365
+ }
366
+
367
+ /* Content Tabs */
368
+ .content-tab {
369
+ display: none !important;
370
+ opacity: 0;
371
+ transform: translateY(20px);
372
+ transition: opacity 0.5s ease-out, transform 0.5s ease-out;
373
+ }
374
+
375
+ .content-tab.active-tab {
376
+ display: block !important;
377
+ opacity: 1;
378
+ transform: translateY(0);
379
+ }
380
+
381
+ .tab-title {
382
+ color: var(--primary-color);
383
+ font-weight: 700;
384
+ font-size: 2.25rem;
385
+ margin-bottom: 2rem;
386
+ text-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
387
+ letter-spacing: -0.5px;
388
+ }
389
+
390
+ /* Upload Area */
391
+ .upload-area-glass {
392
+ height: 400px;
393
+ border: 3px dashed var(--glass-border);
394
+ display: flex;
395
+ flex-direction: column;
396
+ justify-content: center;
397
+ align-items: center;
398
+ text-align: center;
399
+ cursor: pointer;
400
+ transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
401
+ background: linear-gradient(135deg, rgba(34, 197, 94, 0.05) 0%, rgba(5, 46, 22, 0.1) 100%);
402
+ }
403
+
404
+ .upload-area-glass.drag-over {
405
+ background: linear-gradient(135deg, rgba(34, 197, 94, 0.15) 0%, rgba(34, 197, 94, 0.05) 100%);
406
+ border-color: #8A9A5B;
407
+ box-shadow: 0 0 40px rgba(34, 197, 94, 0.4);
408
+ transform: scale(1.02);
409
+ }
410
+
411
+ .upload-area-glass .upload-icon {
412
+ display: flex;
413
+ justify-content: center;
414
+ align-items: center;
415
+ }
416
+
417
+ .upload-area-glass .upload-icon i {
418
+ font-size: 5rem;
419
+ color: #8A9A5B;
420
+ /* margin-bottom: 1.5rem; -- removed */
421
+ filter: drop-shadow(0 0 10px rgba(138, 154, 91, 0.4));
422
+ }
423
+
424
+ .upload-area-glass p {
425
+ color: var(--text-light);
426
+ font-size: 1.1rem;
427
+ margin: 0.5rem 0;
428
+ }
429
+
430
+ .upload-area-glass form {
431
+ height: 100%;
432
+ width: 100%;
433
+ display: flex; /* Make the form a flex container too */
434
+ flex-direction: column;
435
+ justify-content: center;
436
+ align-items: center;
437
+ }
438
+
439
+ .upload-area-glass .text-muted {
440
+ color: var(--text-muted);
441
+ }
442
+
443
+ /* Image Preview */
444
+ #image-preview {
445
+ max-height: 500px;
446
+ width: 100%;
447
+ object-fit: cover;
448
+ border-radius: var(--border-radius-sm);
449
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
450
+ }
451
+
452
+ /* Diagnosis Results */
453
+ #disease-name {
454
+ font-weight: 700;
455
+ color: var(--primary-green);
456
+ font-size: 1.75rem;
457
+ margin-bottom: 1rem;
458
+ text-shadow: 0 0 15px var(--green-glow);
459
+ }
460
+
461
+ #health-status {
462
+ color: var(--text-light);
463
+ font-size: 1.1rem;
464
+ line-height: 1.6;
465
+ }
466
+
467
+ #detailed-symptoms ul,
468
+ #prevention-methods ul {
469
+ padding-left: 1.5rem;
470
+ color: var(--text-light);
471
+ }
472
+
473
+ #detailed-symptoms li,
474
+ #prevention-methods li {
475
+ margin-bottom: 0.75rem;
476
+ line-height: 1.6;
477
+ }
478
+
479
+ /* Chat Tab */
480
+ .chat-container {
481
+ display: flex;
482
+ flex-direction: column;
483
+ padding-left: 1.5rem;
484
+ height: 100%;
485
+ width: 100%; /* Ensure it takes full width */
486
+ }
487
+
488
+ .chat-box-glass {
489
+ flex-grow: 1;
490
+ padding: 2rem;
491
+ overflow-y: auto;
492
+ margin-bottom: 1.5rem;
493
+ min-height: 400px;
494
+ max-height: calc(100vh - 250px);
495
+ }
496
+
497
+ .chat-message {
498
+ padding: 1rem 1.5rem;
499
+ border-radius: var(--border-radius-sm);
500
+ margin-bottom: 1rem;
501
+ max-width: 75%;
502
+ line-height: 1.7;
503
+ font-size: 0.95rem;
504
+ animation: fadeIn 0.3s ease-out; /* Changed to fadeIn */
505
+ background: var(--glass-bg); /* Neutral glassmorphism background */
506
+ backdrop-filter: blur(15px);
507
+ -webkit-backdrop-filter: blur(15px);
508
+ border: 1px solid var(--glass-border); /* Neutral glassmorphism border */
509
+ box-shadow: 0 4px 12px var(--glass-shadow); /* Consistent shadow */
510
+ }
511
+
512
+ @keyframes fadeIn {
513
+ from {
514
+ opacity: 0;
515
+ transform: translateY(5px);
516
+ }
517
+ to {
518
+ opacity: 1;
519
+ transform: translateY(0);
520
+ }
521
+ }
522
+
523
+ .chat-message.user {
524
+ color: white;
525
+ margin-left: auto;
526
+ margin-right: 0; /* Ensure right alignment */
527
+ border-bottom-right-radius: 4px;
528
+ border-top-right-radius: var(--border-radius-sm); /* Adjust for consistent corner */
529
+ background: var(--glass-bg-strong); /* Slightly stronger glass for user */
530
+ border-color: rgba(34, 197, 94, 0.4); /* Greenish border for user */
531
+ box-shadow: 0 4px 12px rgba(34, 197, 94, 0.4); /* Greenish shadow for user */
532
+ }
533
+
534
+ .chat-message.bot {
535
+ color: var(--text-light);
536
+ margin-right: auto;
537
+ margin-left: 0; /* Ensure left alignment */
538
+ border-bottom-left-radius: 4px;
539
+ border-top-left-radius: var(--border-radius-sm); /* Adjust for consistent corner */
540
+ background: var(--glass-bg); /* Revert to base glass-bg */
541
+ border-color: var(--glass-border); /* Revert to base glass-border */
542
+ white-space: pre-wrap; /* Preserve formatting */
543
+ animation: none; /* Remove bot-pulse animation */
544
+ }
545
+
546
+ /* Remove bot-pulse keyframes as it's no longer used */
547
+
548
+ /* Thinking Dots Animation */
549
+ @keyframes fadeInOut {
550
+ 0%, 100% { opacity: 0.3; }
551
+ 50% { opacity: 1; }
552
+ }
553
+
554
+ .thinking-dots .dot {
555
+ opacity: 0.3;
556
+ animation: fadeInOut 1.4s infinite;
557
+ display: inline-block;
558
+ width: 6px;
559
+ height: 6px;
560
+ border-radius: 50%;
561
+ background-color: var(--text-light);
562
+ margin: 0 1px;
563
+ }
564
+
565
+ .thinking-dots .dot:nth-child(1) { animation-delay: 0s; }
566
+ .thinking-dots .dot:nth-child(2) { animation-delay: 0.2s; }
567
+ .thinking-dots .dot:nth-child(3) { animation-delay: 0.4s; }
568
+
569
+ .chat-message.bot {
570
+ color: var(--text-light);
571
+ margin-right: auto;
572
+ margin-left: 0; /* Ensure left alignment */
573
+ border-bottom-left-radius: 4px;
574
+ border-top-left-radius: var(--border-radius-sm); /* Adjust for consistent corner */
575
+ background: var(--glass-bg); /* Revert to base glass-bg */
576
+ border-color: var(--glass-border); /* Revert to base glass-border */
577
+ white-space: pre-wrap; /* Preserve formatting */
578
+ animation: none; /* Remove bot-pulse animation */
579
+
580
+ /* New styles for bot message bubbles */
581
+ padding: 12px;
582
+ border-radius: 15px;
583
+ }
584
+
585
+ .bot-message {
586
+ white-space: pre-wrap;
587
+ /* Existing bot-message styles */
588
+ }
589
+
590
+ /* Markdown styling within bot messages */
591
+ .bot-message h1, .bot-message h2, .bot-message h3, .bot-message h4, .bot-message h5, .bot-message h6 {
592
+ color: var(--primary-green);
593
+ margin-top: 1.5em;
594
+ margin-bottom: 0.5em;
595
+ font-weight: 700;
596
+ line-height: 1.2;
597
+ }
598
+
599
+ .bot-message h3 {
600
+ font-size: 1.1em;
601
+ margin-top: 10px;
602
+ }
603
+
604
+ .bot-message ul {
605
+ padding-left: 20px;
606
+ margin-bottom: 1em;
607
+ }
608
+
609
+ .bot-message ol {
610
+ padding-left: 20px;
611
+ margin-bottom: 1em;
612
+ }
613
+
614
+ .bot-message li {
615
+ margin-bottom: 0.5em;
616
+ line-height: 1.6;
617
+ }
618
+
619
+ .bot-message p {
620
+ margin-bottom: 8px;
621
+ line-height: 1.6;
622
+ }
623
+
624
+ .bot-message a {
625
+ color: var(--emerald-green);
626
+ text-decoration: underline;
627
+ }
628
+
629
+ .bot-message strong {
630
+ font-weight: 700;
631
+ color: var(--primary-color);
632
+ }
633
+
634
+ .bot-message em {
635
+ font-style: italic;
636
+ }
637
+
638
+ .bot-message code {
639
+ background-color: rgba(34, 197, 94, 0.1);
640
+ color: var(--emerald-green);
641
+ padding: 0.2em 0.4em;
642
+ border-radius: 4px;
643
+ font-family: 'Fira Code', 'Roboto Mono', monospace;
644
+ font-size: 0.85em;
645
+ }
646
+
647
+ .bot-message pre {
648
+ background-color: rgba(0, 0, 0, 0.3);
649
+ border: 1px solid rgba(255, 255, 255, 0.1);
650
+ border-radius: 8px;
651
+ padding: 1em;
652
+ overflow-x: auto;
653
+ font-family: 'Fira Code', 'Roboto Mono', monospace;
654
+ font-size: 0.85em;
655
+ margin-bottom: 1em;
656
+ }
657
+
658
+ .bot-message blockquote {
659
+ border-left: 4px solid var(--primary-green);
660
+ padding-left: 1em;
661
+ margin-left: 0;
662
+ color: var(--text-muted);
663
+ font-style: italic;
664
+ }
665
+
666
+ .bot-message hr {
667
+ border: none;
668
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
669
+ margin: 1.5em 0;
670
+ }
671
+
672
+ /* --- Model Showcase --- */
673
+ .showcase-grid {
674
+ display: grid;
675
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
676
+ gap: 1.5rem;
677
+ padding: 1rem 0;
678
+ }
679
+
680
+ .showcase-btn {
681
+ background: var(--glass-bg);
682
+ border: 1.5px solid var(--glass-border);
683
+ border-radius: var(--border-radius-sm);
684
+ padding: 1.5rem;
685
+ color: var(--text-light);
686
+ font-weight: 600;
687
+ text-align: center;
688
+ cursor: pointer;
689
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
690
+ position: relative;
691
+ overflow: hidden;
692
+ will-change: transform, box-shadow;
693
+ }
694
+
695
+ .showcase-btn::before {
696
+ content: '';
697
+ position: absolute;
698
+ top: 50%;
699
+ left: 50%;
700
+ width: 0;
701
+ height: 0;
702
+ background: var(--green-glow);
703
+ border-radius: 50%;
704
+ transform: translate(-50%, -50%);
705
+ transition: width 0.4s, height 0.4s;
706
+ opacity: 0;
707
+ z-index: 0;
708
+ }
709
+
710
+ .showcase-btn:hover::before,
711
+ .showcase-btn:focus::before {
712
+ width: 200%;
713
+ height: 200%;
714
+ opacity: 1;
715
+ }
716
+
717
+ .showcase-btn:hover {
718
+ transform: translateY(-5px) scale(1.03);
719
+ border-color: var(--primary-green);
720
+ color: var(--primary-color);
721
+ box-shadow: 0 8px 30px rgba(34, 197, 94, 0.3);
722
+ }
723
+
724
+ .showcase-btn-title {
725
+ position: relative;
726
+ z-index: 1;
727
+ font-size: 1rem;
728
+ pointer-events: none; /* So it doesn't interfere with the button click */
729
+ }
730
+
731
+ /* Showcase Overlay & Card */
732
+ .showcase-overlay {
733
+ position: fixed;
734
+ top: 0;
735
+ left: 0;
736
+ width: 100%;
737
+ height: 100%;
738
+ z-index: 1050;
739
+ display: flex;
740
+ justify-content: center;
741
+ align-items: center;
742
+ background-color: rgba(0, 0, 0, 0.3);
743
+ backdrop-filter: blur(0px);
744
+ -webkit-backdrop-filter: blur(0px);
745
+ opacity: 0;
746
+ transition: opacity 0.5s cubic-bezier(0.4, 0, 0.2, 1), backdrop-filter 0.5s cubic-bezier(0.4, 0, 0.2, 1);
747
+ -webkit-transition: opacity 0.5s cubic-bezier(0.4, 0, 0.2, 1), -webkit-backdrop-filter 0.5s cubic-bezier(0.4, 0, 0.2, 1);
748
+ }
749
+
750
+ .showcase-overlay.visible {
751
+ opacity: 1;
752
+ backdrop-filter: blur(15px);
753
+ -webkit-backdrop-filter: blur(15px);
754
+ }
755
+
756
+ .showcase-card {
757
+ position: fixed;
758
+ background: rgba(20, 28, 46, 0.95); /* Slightly darker for better text read */
759
+ backdrop-filter: blur(40px) saturate(180%);
760
+ -webkit-backdrop-filter: blur(40px) saturate(180%);
761
+ border: 1.5px solid var(--glass-border);
762
+ border-radius: var(--border-radius);
763
+ box-shadow: 0 16px 70px rgba(0, 0, 0, 0.5);
764
+ z-index: 1051;
765
+ overflow: hidden;
766
+ opacity: 0;
767
+ will-change: transform, width, height, top, left, opacity;
768
+ transition: all 0.6s cubic-bezier(0.45, 0, 0.1, 1);
769
+
770
+ /* --- KEY CHANGES FOR LANDSCAPE WIDTH --- */
771
+ width: 850px; /* 300px was too small. 850px is wide/landscape. */
772
+ max-width: 95vw; /* Ensures it fits on smaller screens if needed */
773
+ min-height: 350px;
774
+
775
+ /* Centering logic (optional, ensures it stays center) */
776
+ top: 50%;
777
+ left: 50%;
778
+ transform: translate(-50%, -50%);
779
+
780
+ display: flex;
781
+ flex-direction: column;
782
+ }
783
+
784
+ .showcase-card.expanding {
785
+ opacity: 1;
786
+ }
787
+
788
+ .showcase-card.expanded {
789
+ width: 850px !important; /* Explicitly set fixed width */
790
+ height: 700px; /* Explicitly set fixed height */
791
+ max-width: 90vw; /* Responsive constraint */
792
+ max-height: 90vh; /* Responsive constraint */
793
+ top: 50% !important;
794
+ left: 50% !important;
795
+ transform: translate(-50%, -50%) !important;
796
+ margin: 0 !important; /* Remove external offsets */
797
+ opacity: 1;
798
+ }
799
+
800
+ .showcase-content {
801
+ padding: 3rem; /* Increased padding */
802
+ height: 100%;
803
+ overflow-y: auto; /* Added for scrollability */
804
+ opacity: 0;
805
+ transform: translateY(20px);
806
+ transition: opacity 0.4s 0.3s ease-out, transform 0.4s 0.3s ease-out;
807
+ }
808
+
809
+ /* Custom scrollbar for showcase-content */
810
+ .showcase-content::-webkit-scrollbar {
811
+ width: 8px;
812
+ }
813
+
814
+ .showcase-content::-webkit-scrollbar-track {
815
+ background: rgba(255, 255, 255, 0.05);
816
+ border-radius: 10px;
817
+ }
818
+
819
+ .showcase-content::-webkit-scrollbar-thumb {
820
+ background-color: var(--primary-green);
821
+ border-radius: 10px;
822
+ border: 2px solid rgba(255, 255, 255, 0.1);
823
+ }
824
+
825
+ .showcase-content::-webkit-scrollbar-thumb:hover {
826
+ background-color: var(--emerald-green);
827
+ }
828
+
829
+ .showcase-card.expanded .showcase-content {
830
+ opacity: 1;
831
+ transform: translateY(0);
832
+ }
833
+
834
+ .btn-close-glass {
835
+ position: absolute;
836
+ top: 20px;
837
+ right: 20px;
838
+ width: 40px;
839
+ height: 40px;
840
+ background: rgba(255, 255, 255, 0.1);
841
+ border: 1px solid rgba(255, 255, 255, 0.2);
842
+ border-radius: 50%;
843
+ color: white;
844
+ font-size: 24px;
845
+ line-height: 38px;
846
+ text-align: center;
847
+ cursor: pointer;
848
+ transition: all 0.3s;
849
+ z-index: 10;
850
+ opacity: 0;
851
+ transform: scale(0.5);
852
+ transition: opacity 0.3s 0.5s, transform 0.3s 0.5s;
853
+ }
854
+
855
+ .showcase-card.expanded .btn-close-glass {
856
+ opacity: 1;
857
+ transform: scale(1);
858
+ }
859
+
860
+ .btn-close-glass:hover {
861
+ background: rgba(255, 0, 0, 0.3);
862
+ transform: scale(1.1) rotate(90deg);
863
+ }
864
+
865
+ /* Showcase Content Typography */
866
+ .showcase-content h2 {
867
+ color: var(--primary-green);
868
+ font-weight: 700;
869
+ margin-bottom: 0.5rem;
870
+ text-shadow: 0 0 10px var(--green-glow);
871
+ font-size: 1.8rem; /* Reset font size for title */
872
+ }
873
+
874
+ .showcase-content p,
875
+ .showcase-content ul li {
876
+ white-space: normal; /* Ensure text wraps naturally */
877
+ }
878
+
879
+
static/images/green-leaves-3840x2160-23231.jpg ADDED

Git LFS Details

  • SHA256: 43afc3d982e32969a99bea3f5034e7e7f61ef21aced2304b502e799e2bd645de
  • Pointer size: 132 Bytes
  • Size of remote file: 1.92 MB
static/js/main.js ADDED
@@ -0,0 +1,440 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ document.addEventListener('DOMContentLoaded', function () {
3
+ // --- Globals & Element Selectors ---
4
+ const sidebar = document.getElementById('sidebar');
5
+ const content = document.getElementById('content');
6
+ const sidebarCollapse = document.getElementById('sidebarCollapse');
7
+ const navLinks = document.querySelectorAll('.sidebar-nav .list-unstyled a');
8
+ const tabs = document.querySelectorAll('.content-tab');
9
+
10
+ // Diagnose Tab Elements
11
+ const uploadArea = document.getElementById('upload-area');
12
+ const imageUploadInput = document.getElementById('image-upload');
13
+ const loadingSpinner = document.getElementById('loading-spinner');
14
+ const diagnosisResult = document.getElementById('diagnosis-result');
15
+ const imagePreview = document.getElementById('image-preview');
16
+ const diseaseName = document.getElementById('disease-name');
17
+ const healthStatus = document.getElementById('health-status');
18
+ const sourceModelBadge = document.getElementById('source-model-badge');
19
+ const detailedSymptoms = document.getElementById('detailed-symptoms');
20
+ const preventionMethods = document.getElementById('prevention-methods');
21
+ const smartAnalysis = document.getElementById('smart-analysis');
22
+ const diagnoseNewImageBtn = document.getElementById('diagnose-new-image-btn');
23
+
24
+ // Analysis Tab Elements
25
+ const placeholderMsg = document.getElementById('placeholder-msg');
26
+ const analysisLoader = document.getElementById('analysis-loader');
27
+ const analysisDashboard = document.getElementById('analysis-dashboard');
28
+
29
+ // Chat Tab Elements
30
+ const chatForm = document.getElementById('chat-form');
31
+ const chatInput = document.getElementById('chat-input');
32
+ const chatBox = document.getElementById('chat-box');
33
+
34
+ // Showcase Tab Elements
35
+ const showcaseGrid = document.getElementById('showcase-grid');
36
+ const showcaseOverlay = document.getElementById('showcase-overlay');
37
+ const showcaseCard = document.getElementById('showcase-card');
38
+ const showcaseContent = document.getElementById('showcase-content');
39
+ const showcaseCloseBtn = document.getElementById('showcase-close-btn');
40
+
41
+ // --- Sidebar Functionality ---
42
+ sidebarCollapse.addEventListener('click', () => {
43
+ sidebar.classList.toggle('collapsed');
44
+ content.classList.toggle('full-width');
45
+ });
46
+
47
+ // --- Tab Navigation ---
48
+ navLinks.forEach(link => {
49
+ link.parentElement.addEventListener('click', (e) => {
50
+ e.preventDefault();
51
+ const tabId = link.parentElement.getAttribute('data-tab');
52
+
53
+ // Update active link
54
+ document.querySelector('.sidebar-nav .list-unstyled .active').classList.remove('active');
55
+ link.parentElement.classList.add('active');
56
+
57
+ // Update active tab
58
+ tabs.forEach(tab => {
59
+ tab.classList.remove('active-tab');
60
+ });
61
+ const targetTab = document.getElementById(`${tabId}-tab`);
62
+ targetTab.classList.add('active-tab');
63
+
64
+ // Smart Tab Listener: Analysis tab logic
65
+ if (tabId === 'analysis') {
66
+ // Check if imagePreview.src contains a valid image (not #)
67
+ if (imagePreview.src && imagePreview.src !== '#') {
68
+ // Only fetch if dashboard is not currently displayed or its src is empty
69
+ if (analysisDashboard.style.display === 'none' || !analysisDashboard.src || analysisDashboard.src === window.location.href + '#') {
70
+ placeholderMsg.style.display = 'none';
71
+ analysisLoader.style.display = 'block';
72
+ analysisDashboard.style.display = 'none';
73
+
74
+ fetch('/visualize')
75
+ .then(response => {
76
+ if (!response.ok) {
77
+ return response.json().then(err => {
78
+ throw new Error(err.error || `HTTP error! Status: ${response.status}`);
79
+ });
80
+ }
81
+ return response.json();
82
+ })
83
+ .then(data => {
84
+ if (data.graph) {
85
+ // Inject the Base64 string into #analysis-dashboard
86
+ analysisDashboard.src = data.graph;
87
+ analysisDashboard.style.display = 'block';
88
+ placeholderMsg.style.display = 'none';
89
+ } else {
90
+ throw new Error("No graph data received from server.");
91
+ }
92
+ })
93
+ .catch(error => {
94
+ console.error('Analysis Fetch Error:', error);
95
+ placeholderMsg.innerHTML = `<p class="text-light">Error: ${error.message}</p>`;
96
+ placeholderMsg.style.display = 'block';
97
+ analysisDashboard.style.display = 'none';
98
+ })
99
+ .finally(() => {
100
+ // Hide loader
101
+ analysisLoader.style.display = 'none';
102
+ });
103
+ }
104
+ // If already visible and has content, do nothing, just make sure other elements are hidden
105
+ else {
106
+ placeholderMsg.style.display = 'none';
107
+ analysisLoader.style.display = 'none';
108
+ analysisDashboard.style.display = 'block'; // Ensure it's visible if already loaded
109
+ }
110
+ } else {
111
+ // No valid image - show placeholder
112
+ placeholderMsg.style.display = 'block';
113
+ analysisLoader.style.display = 'none';
114
+ analysisDashboard.style.display = 'none';
115
+ }
116
+ }
117
+ });
118
+ });
119
+
120
+ // --- Diagnose Tab: File Upload ---
121
+ uploadArea.addEventListener('click', () => imageUploadInput.click());
122
+
123
+ uploadArea.addEventListener('dragover', (e) => {
124
+ e.preventDefault();
125
+ uploadArea.classList.add('drag-over');
126
+ });
127
+
128
+ uploadArea.addEventListener('dragleave', () => {
129
+ uploadArea.classList.remove('drag-over');
130
+ });
131
+
132
+ uploadArea.addEventListener('drop', (e) => {
133
+ e.preventDefault();
134
+ uploadArea.classList.remove('drag-over');
135
+ const files = e.dataTransfer.files;
136
+ if (files.length > 0) {
137
+ imageUploadInput.files = files;
138
+ handleImageUpload(files[0]);
139
+ }
140
+ });
141
+
142
+ imageUploadInput.addEventListener('change', (e) => {
143
+ if (e.target.files.length > 0) {
144
+ handleImageUpload(e.target.files[0]);
145
+ }
146
+ });
147
+
148
+ // Event listener for the new "Diagnose New Image" button
149
+ diagnoseNewImageBtn.addEventListener('click', () => {
150
+ diagnosisResult.style.display = 'none'; // Hide results
151
+ imagePreview.src = '#'; // Clear image preview
152
+ // Clear previous data
153
+ diseaseName.textContent = '';
154
+ healthStatus.textContent = '';
155
+ detailedSymptoms.innerHTML = '';
156
+ preventionMethods.innerHTML = '';
157
+ smartAnalysis.textContent = '';
158
+ sourceModelBadge.style.display = 'none';
159
+
160
+ // Reset analysis dashboard state
161
+ analysisDashboard.style.display = 'none';
162
+ analysisDashboard.src = '#';
163
+
164
+ uploadArea.style.display = 'flex'; // Show upload area
165
+ imageUploadInput.value = ''; // Clear file input
166
+ });
167
+
168
+ async function handleImageUpload(file) {
169
+ if (!file.type.startsWith('image/')) {
170
+ alert('Please upload a valid image file.');
171
+ return;
172
+ }
173
+
174
+ // Show loading state
175
+ loadingSpinner.style.display = 'block';
176
+ diagnosisResult.style.display = 'none';
177
+ uploadArea.style.display = 'none';
178
+
179
+ // Display image preview
180
+ const reader = new FileReader();
181
+ reader.onload = e => imagePreview.src = e.target.result;
182
+ reader.readAsDataURL(file);
183
+
184
+ // Prepare form data and send to backend
185
+ const formData = new FormData();
186
+ formData.append('file', file);
187
+
188
+ try {
189
+ const response = await fetch('/diagnose', {
190
+ method: 'POST',
191
+ body: formData
192
+ });
193
+
194
+ if (!response.ok) {
195
+ throw new Error(`HTTP error! Status: ${response.status}`);
196
+ }
197
+
198
+ const data = await response.json();
199
+ displayDiagnosis(data);
200
+
201
+ } catch (error) {
202
+ console.error('Diagnosis Error:', error);
203
+ displayError('Failed to get diagnosis. Please ensure the backend is running and check the console.');
204
+ } finally {
205
+ // Hide loading state and show results
206
+ loadingSpinner.style.display = 'none';
207
+ diagnosisResult.style.display = 'block';
208
+ }
209
+ }
210
+
211
+ function displayError(message) {
212
+ uploadArea.style.display = 'flex'; // Show upload area again
213
+ diseaseName.textContent = 'Error';
214
+ healthStatus.textContent = message;
215
+ detailedSymptoms.innerHTML = '';
216
+ preventionMethods.innerHTML = '';
217
+ smartAnalysis.textContent = '';
218
+ sourceModelBadge.style.display = 'none';
219
+ diagnosisResult.style.display = 'block';
220
+ }
221
+
222
+
223
+ function displayDiagnosis(data) {
224
+ // Format text with bullet points
225
+ const formatBulletPoints = (text) => {
226
+ if (!text) return '';
227
+ return '<ul>' + text.split('\\n').map(item => item.trim().startsWith('-') ? `<li>${item.substring(1).trim()}</li>` : `<li>${item.trim()}</li>`).join('') + '</ul>';
228
+ };
229
+
230
+ diseaseName.textContent = data.disease_name || 'N/A';
231
+ healthStatus.textContent = data.health_status || 'N/A';
232
+ smartAnalysis.textContent = data.smart_analysis || 'N/A';
233
+
234
+ detailedSymptoms.innerHTML = formatBulletPoints(data.detailed_symptoms);
235
+ preventionMethods.innerHTML = formatBulletPoints(data.prevention_methods);
236
+
237
+ // Update source model badge
238
+ if (data.source_model) {
239
+ sourceModelBadge.textContent = `Source: ${data.source_model}`;
240
+ sourceModelBadge.className = 'badge'; // Reset class
241
+ if (data.source_model === 'Keras + DB') {
242
+ sourceModelBadge.classList.add('bg-success');
243
+ } else {
244
+ sourceModelBadge.classList.add('bg-info');
245
+ }
246
+ sourceModelBadge.style.display = 'inline-block';
247
+ }
248
+ }
249
+
250
+ // --- Chat Tab: Chat Functionality ---
251
+ chatForm.addEventListener('submit', async function(e) {
252
+ e.preventDefault();
253
+ const message = chatInput.value.trim();
254
+ if (!message) return;
255
+
256
+ // Append user message immediately
257
+ appendMessage(message, 'user');
258
+ chatInput.value = ''; // Clear input
259
+
260
+ // Create a new message bubble for the bot's response and indicate loading
261
+ const botDiv = appendMessage('', 'bot'); // Append '' initially
262
+ // Add pulsing dots
263
+ const thinkingIndicator = document.createElement('div');
264
+ thinkingIndicator.className = 'thinking-dots';
265
+ thinkingIndicator.innerHTML = '<span class="dot">.</span><span class="dot">.</span><span class="dot">.</span>';
266
+ botDiv.appendChild(thinkingIndicator);
267
+ chatBox.scrollTop = chatBox.scrollHeight;
268
+
269
+ try {
270
+ const response = await fetch('/chat', {
271
+ method: 'POST',
272
+ headers: { 'Content-Type': 'application/json' },
273
+ body: JSON.stringify({ message: message })
274
+ });
275
+
276
+ if (!response.ok) {
277
+ const errorText = await response.text();
278
+ throw new Error(`HTTP error! Status: ${response.status}, Message: ${errorText}`);
279
+ }
280
+
281
+ const botResponseText = await response.text(); // Get the full response text
282
+ botDiv.innerHTML = ''; // Clear thinking indicator
283
+ botDiv.innerHTML = marked.parse(botResponseText); // Use marked.parse for HTML rendering
284
+ chatBox.scrollTop = chatBox.scrollHeight; // Scroll to the bottom
285
+
286
+ } catch (error) {
287
+ console.error('Bytez API Error:', error);
288
+ botDiv.innerHTML = 'Sorry, I am having trouble connecting to the AI. Please try again later. ' + error.message; // Use innerHTML for error
289
+ chatBox.scrollTop = chatBox.scrollHeight;
290
+ }
291
+ }); // This closes the chatForm.addEventListener
292
+
293
+ function appendMessage(message, sender) {
294
+ const messageDiv = document.createElement('div');
295
+ messageDiv.className = `chat-message ${sender}`;
296
+ if (sender === 'bot') {
297
+ messageDiv.classList.add('bot-message'); // Add bot-message class for styling
298
+ }
299
+ messageDiv.textContent = message;
300
+ chatBox.appendChild(messageDiv);
301
+ chatBox.scrollTop = chatBox.scrollHeight;
302
+ return messageDiv;
303
+ }
304
+
305
+ // --- Model Showcase Tab: Interactive Expansion ---
306
+ let activeCard = null;
307
+
308
+ async function loadModelClasses() {
309
+ if (!showcaseGrid) return;
310
+ try {
311
+ const response = await fetch('/api/classes');
312
+ if (!response.ok) throw new Error('Failed to fetch classes');
313
+ const classes = await response.json();
314
+
315
+ if (classes && !classes.error) {
316
+ showcaseGrid.innerHTML = ''; // Clear previous
317
+ classes.forEach(className => {
318
+ const btn = document.createElement('button');
319
+ btn.className = 'showcase-btn';
320
+ // The className is now the source of truth, e.g., "Apple Apple Scab"
321
+ btn.dataset.classId = className;
322
+
323
+ const title = document.createElement('span');
324
+ title.className = 'showcase-btn-title';
325
+ // The text content is the same as the ID
326
+ title.textContent = className;
327
+
328
+ btn.appendChild(title);
329
+ btn.addEventListener('click', () => openShowcaseCard(btn));
330
+ showcaseGrid.appendChild(btn);
331
+ });
332
+ } else {
333
+ showcaseGrid.innerHTML = '<p class="text-danger">Could not load model classes.</p>';
334
+ }
335
+ } catch (error) {
336
+ console.error('Error fetching model classes:', error);
337
+ showcaseGrid.innerHTML = '<p class="text-danger">Could not load model classes.</p>';
338
+ }
339
+ }
340
+
341
+ async function openShowcaseCard(button) {
342
+ if (activeCard) return;
343
+ activeCard = button;
344
+
345
+ const classId = button.dataset.classId;
346
+ const rect = button.getBoundingClientRect();
347
+
348
+ // 1. Show overlay
349
+ showcaseOverlay.style.display = 'flex';
350
+ requestAnimationFrame(() => {
351
+ showcaseOverlay.classList.add('visible');
352
+ });
353
+
354
+ // 2. Set initial card position and size
355
+ showcaseCard.style.top = `${rect.top}px`;
356
+ showcaseCard.style.left = `${rect.left}px`;
357
+ showcaseCard.style.width = `${rect.width}px`;
358
+ showcaseCard.style.height = `${rect.height}px`;
359
+
360
+ // 3. Fetch data and prepare content
361
+ showcaseContent.innerHTML = '<div class="spinner-border text-light" role="status"><span class="visually-hidden">Loading...</span></div>';
362
+ // Use encodeURIComponent to handle spaces and special characters in the classId
363
+ const dataPromise = fetch(`/api/showcase/${encodeURIComponent(classId)}`).then(res => res.json());
364
+
365
+ // 4. Start expansion animation
366
+ requestAnimationFrame(() => {
367
+ showcaseCard.classList.add('expanding');
368
+ requestAnimationFrame(() => {
369
+ showcaseCard.classList.add('expanded');
370
+ });
371
+ });
372
+
373
+ // 5. Populate content when data arrives
374
+ try {
375
+ const data = await dataPromise;
376
+ if (data.error) throw new Error(data.error);
377
+
378
+ showcaseContent.innerHTML = `
379
+ <h2>${data.title}</h2>
380
+ <p>${data.short_description}</p>
381
+ <div class="row">
382
+ <div class="col-md-6">
383
+ <h3><i class="fa-solid fa-microscope me-2"></i>Quick Symptoms</h3>
384
+ <ul>${data.quick_symptoms.map(s => `<li>${s}</li>`).join('')}</ul>
385
+ </div>
386
+ <div class="col-md-6">
387
+ <h3><i class="fa-solid fa-shield-alt me-2"></i>Fast Prevention</h3>
388
+ <ul>${data.fast_prevention.map(p => `<li>${p}</li>`).join('')}</ul>
389
+ </div>
390
+ </div>
391
+ `;
392
+ } catch (error) {
393
+ showcaseContent.innerHTML = `<p class="text-danger">Error: ${error.message}</p>`;
394
+ }
395
+ }
396
+
397
+ function closeShowcaseCard() {
398
+ if (!activeCard) return;
399
+
400
+ const rect = activeCard.getBoundingClientRect();
401
+
402
+ // Reverse the animation
403
+ showcaseCard.classList.remove('expanded');
404
+
405
+ // Reset to button's position - needs to be done after the transition starts
406
+ setTimeout(() => {
407
+ showcaseCard.style.top = `${rect.top}px`;
408
+ showcaseCard.style.left = `${rect.left}px`;
409
+ showcaseCard.style.width = `${rect.width}px`;
410
+ showcaseCard.style.height = `${rect.height}px`;
411
+ }, 0);
412
+
413
+ showcaseOverlay.classList.remove('visible');
414
+
415
+ // Hide elements after transition
416
+ showcaseCard.addEventListener('transitionend', () => {
417
+ showcaseCard.classList.remove('expanding');
418
+ showcaseOverlay.style.display = 'none';
419
+ showcaseContent.innerHTML = '';
420
+ activeCard = null;
421
+ }, { once: true });
422
+ }
423
+
424
+ // Event listeners for closing the card
425
+ showcaseCloseBtn.addEventListener('click', closeShowcaseCard);
426
+ showcaseOverlay.addEventListener('click', (e) => {
427
+ if (e.target === showcaseOverlay) { // Only if clicking the background itself
428
+ closeShowcaseCard();
429
+ }
430
+ });
431
+ document.addEventListener('keydown', (e) => {
432
+ if (e.key === 'Escape' && activeCard) {
433
+ closeShowcaseCard();
434
+ }
435
+ });
436
+
437
+ // --- Initializations ---
438
+ loadModelClasses();
439
+ });
440
+
static/uploads/00d12ac0-a293-47e0-a4c6-a80f37204c39___FREC_Pwd.M_4812.jpg ADDED

Git LFS Details

  • SHA256: 9988d16b24722dce3d764387712b6c7825e9c01732eb5e017ffa78593c47c890
  • Pointer size: 129 Bytes
  • Size of remote file: 9.04 kB
static/uploads/0a0dbf1f-1131-496f-b337-169ec6693e6f___NREC_B.Spot_9241.JPG ADDED

Git LFS Details

  • SHA256: 0e70e39a00c34361880a4f2e111bffed00ac492d4b293d41bdbc6ec3a5ec0fdd
  • Pointer size: 130 Bytes
  • Size of remote file: 19.9 kB
static/uploads/1a146bf4-5696-4e19-842e-9e0e1ac7ac6d___RS_LB_5023.JPG ADDED

Git LFS Details

  • SHA256: 914317fa91b27fe3d641eed9d9f0e7afbfad72331f005ec89e792a87c30dc78f
  • Pointer size: 130 Bytes
  • Size of remote file: 14 kB
static/uploads/4b1d558e-7ffb-409b-bf8e-9b665519e896___Com.G_SpM_FL_8746.JPG ADDED

Git LFS Details

  • SHA256: 44059d2c96d55c1302786278b6a96d598959db0cf2f4370dd16ab2d4418154e2
  • Pointer size: 130 Bytes
  • Size of remote file: 20.6 kB
static/uploads/4f44e677-ae55-48f2-a439-11d162fcfe3e___Com.G_SpM_FL_8599.JPG ADDED

Git LFS Details

  • SHA256: 4742659332cb6db158c5dcae7a0377458f5f42e045dd05ff24dfb76f8cb2d06b
  • Pointer size: 130 Bytes
  • Size of remote file: 20.4 kB
static/uploads/5-common-plant-diseases-and-how-to-treat-them-5.webp ADDED

Git LFS Details

  • SHA256: 94b0b997b4a56d98b30a27fa8fb026e8077f3820c3a56912e87b082db1e612f5
  • Pointer size: 131 Bytes
  • Size of remote file: 140 kB
static/uploads/Keep_Garden-Header.jpg ADDED

Git LFS Details

  • SHA256: 3eae52e04398acee74b0090929895c2e5bb36bcf0dd8b68b4abb68b37e797bc3
  • Pointer size: 130 Bytes
  • Size of remote file: 43.6 kB
static/uploads/Leaf-spot-700x450.webp ADDED

Git LFS Details

  • SHA256: 3bb97b727b3e241e104ac589dec0f91fe0cd934d614260715af12fd012a7ec2e
  • Pointer size: 130 Bytes
  • Size of remote file: 48.3 kB
static/uploads/Potato-leaf-blight.webp ADDED

Git LFS Details

  • SHA256: f9a8a8c8fc613be8a50f8ec1f2b8ed659174cd262bf7a16defd1c3ac18c7b4da
  • Pointer size: 130 Bytes
  • Size of remote file: 25.6 kB
static/uploads/download.jpg ADDED

Git LFS Details

  • SHA256: 992af6b228b314908338d88a445e68758cbc363ae307bdac5348031925c94efb
  • Pointer size: 129 Bytes
  • Size of remote file: 7.75 kB
static/uploads/images.jpg ADDED

Git LFS Details

  • SHA256: 428fb185705ebecc89bce6300f6156f821dbc93c459690973f85b9a8e240c95c
  • Pointer size: 129 Bytes
  • Size of remote file: 5.66 kB
static/uploads/istockphoto-483451251-612x612.jpg ADDED

Git LFS Details

  • SHA256: fd002b3762ae321d6c008dc1da0e4e2c8cb4fff45495ac913fd4bd201a968ba6
  • Pointer size: 130 Bytes
  • Size of remote file: 66.3 kB
static/uploads/marssonia-leaf-spot-on-euonymus-grabowski.jpg ADDED

Git LFS Details

  • SHA256: 8ee04b364e03ab526e61d41df2f84b6f7b4705c09fafda62fc837b5a27c44ebc
  • Pointer size: 131 Bytes
  • Size of remote file: 110 kB
static/uploads/rose-tree-disease-1024x768.jpg ADDED

Git LFS Details

  • SHA256: fad62b37beac626ef640735cc1c6a101940ea6305257d0bbac2bdcfae4563b4c
  • Pointer size: 131 Bytes
  • Size of remote file: 105 kB
static/uploads/spring-plant-diseases.webp ADDED

Git LFS Details

  • SHA256: 4de4759d8775ae084d9bce2c30bd89c352a15b44e7b82dfb7a86aef2623549ef
  • Pointer size: 131 Bytes
  • Size of remote file: 220 kB
static/uploads/ydip_0.jpg ADDED

Git LFS Details

  • SHA256: 0c4829be5fe2b222bc593b9cf66b57855be44a0caa3886be30d4d98e695fdbe6
  • Pointer size: 130 Bytes
  • Size of remote file: 37.7 kB
templates/index.html ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <!DOCTYPE html>
3
+ <html lang="en">
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Plantoi - AI Plant Diagnosis</title>
8
+
9
+ <!-- Bootstrap 5 CSS -->
10
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
11
+
12
+ <!-- FontAwesome Icons -->
13
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css">
14
+
15
+ <!-- Custom CSS -->
16
+ <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
17
+ </head>
18
+ <body>
19
+ <div class="background-animation"></div>
20
+ <div class="main-container">
21
+ <!-- Collapsible Sidebar -->
22
+ <nav id="sidebar" class="sidebar-nav">
23
+ <div class="sidebar-header">
24
+ <h3><i class="fa-solid fa-leaf me-2"></i>Plantoi</h3>
25
+ </div>
26
+ <ul class="list-unstyled components">
27
+ <li class="active" data-tab="diagnose">
28
+ <a href="#"><i class="fa-solid fa-stethoscope"></i><span>Diagnose</span></a>
29
+ </li>
30
+ <li data-tab="analysis">
31
+ <a href="#"><i class="fa-solid fa-chart-pie"></i><span>Visual Analysis</span></a>
32
+ </li>
33
+ <li data-tab="chat">
34
+ <a href="#"><i class="fa-solid fa-comments"></i><span>Ask AI</span></a>
35
+ </li>
36
+ <li data-tab="showcase">
37
+ <a href="#"><i class="fa-solid fa-microscope"></i><span>Model Showcase</span></a>
38
+ </li>
39
+ </ul>
40
+ <div class="sidebar-toggler">
41
+ <button type="button" id="sidebarCollapse" class="btn btn-glass">
42
+ <i class="fas fa-arrow-left"></i>
43
+ </button>
44
+ </div>
45
+ </nav>
46
+
47
+ <!-- Page Content -->
48
+ <div id="content" class="flex-grow-1">
49
+ <!-- Diagnose Tab -->
50
+ <div id="diagnose-tab" class="content-tab active-tab">
51
+ <div class="container-fluid">
52
+ <h2 class="tab-title">Plant Disease Diagnosis</h2>
53
+ <div id="upload-area" class="upload-area-glass">
54
+ <form class="h-100">
55
+ <div class="upload-icon">
56
+ <i class="fas fa-cloud-upload-alt"></i>
57
+ </div>
58
+ <p><strong>Drag & Drop your plant image here</strong></p>
59
+ <p class="text-muted">or click to select a file</p>
60
+ <input type="file" id="image-upload" accept="image/*" hidden>
61
+ </form>
62
+ </div>
63
+ <div id="diagnosis-result" class="mt-4" style="display: none;">
64
+ <div class="row">
65
+ <div class="col-lg-4 mb-4">
66
+ <div class="glass-card">
67
+ <h5 class="card-header-glass"><i class="fa-solid fa-image me-2"></i>Preview</h5>
68
+ <img id="image-preview" src="#" alt="Image Preview" class="img-fluid rounded">
69
+ </div>
70
+ </div>
71
+ <div class="col-lg-8">
72
+ <div class="glass-card mb-4">
73
+ <h5 class="card-header-glass"><i class="fa-solid fa-pills me-2"></i>Diagnosis Report</h5>
74
+ <div class="card-body-glass">
75
+ <h3 id="disease-name"></h3>
76
+ <p id="health-status"></p>
77
+ <span id="source-model-badge" class="badge"></span>
78
+ </div>
79
+ </div>
80
+ <div class="glass-card">
81
+ <div class="card-body-glass">
82
+ <h5><i class="fa-solid fa-notes-medical me-2"></i>Detailed Symptoms</h5>
83
+ <div id="detailed-symptoms"></div>
84
+ <hr>
85
+ <h5><i class="fa-solid fa-shield-virus me-2"></i>Prevention Methods</h5>
86
+ <div id="prevention-methods"></div>
87
+ <hr>
88
+ <h5><i class="fa-solid fa-lightbulb me-2"></i>Smart Analysis</h5>
89
+ <p id="smart-analysis"></p>
90
+ </div>
91
+ </div>
92
+ <button id="diagnose-new-image-btn" class="btn btn-glass mt-4 w-100"><i class="fa-solid fa-redo me-2"></i>Diagnose New Image</button>
93
+ </div>
94
+ </div>
95
+ </div>
96
+ <div id="loading-spinner" class="text-center mt-5" style="display: none;">
97
+ <div class="spinner-border text-light" role="status">
98
+ <span class="visually-hidden">Loading...</span>
99
+ </div>
100
+ <p class="mt-2 text-light">AI is analyzing your plant...</p>
101
+ </div>
102
+ </div>
103
+ </div>
104
+
105
+ <!-- Visual Analysis Tab -->
106
+ <div id="analysis-tab" class="content-tab">
107
+ <div class="container-fluid">
108
+ <h2 class="tab-title">Automated Visual Analysis</h2>
109
+ <div class="glass-card">
110
+ <div id="placeholder-msg" class="card-body-glass text-center">
111
+ <p class="text-light">No diagnosis data found. Please upload a leaf in the Diagnose tab first.</p>
112
+ </div>
113
+ <div id="analysis-loader" class="card-body-glass text-center" style="display: none;">
114
+ <div class="spinner-border text-light" role="status">
115
+ <span class="visually-hidden">Loading...</span>
116
+ </div>
117
+ <p class="mt-2 text-light">Generating health metrics dashboard...</p>
118
+ </div>
119
+ <img id="analysis-dashboard" src="" alt="Analysis Dashboard" style="display:none; width: 100%; border-radius: 15px;">
120
+ </div>
121
+ </div>
122
+ </div>
123
+
124
+ <!-- Ask AI (Chat) Tab -->
125
+ <div id="chat-tab" class="content-tab">
126
+ <div class="d-flex h-100">
127
+
128
+ <!-- Main Chat Area -->
129
+ <div class="chat-container flex-grow-1">
130
+ <div id="chat-box" class="chat-box-glass">
131
+ <!-- Chat messages will be appended here -->
132
+ <div class="chat-message bot">Welcome to the AI Botanist. Ask me anything about your plant's health.</div>
133
+ </div>
134
+ <div class="chat-input-glass">
135
+ <form id="chat-form">
136
+ <input type="text" id="chat-input" class="form-control" placeholder="Ask about your plant...">
137
+ <button type="submit" class="btn btn-glass-send"><i class="fa-solid fa-paper-plane"></i></button>
138
+ </form>
139
+ </div>
140
+ </div>
141
+ </div>
142
+ </div>
143
+
144
+ <!-- Model Showcase Tab -->
145
+ <div id="showcase-tab" class="content-tab">
146
+ <div class="container-fluid">
147
+ <h2 class="tab-title">Interactive Model Showcase</h2>
148
+ <p class="text-light mb-4">The AI model recognizes 38 plant conditions. Click any class to see a detailed diagnosis card.</p>
149
+ <div id="showcase-grid" class="showcase-grid">
150
+ <!-- Glassmorphism buttons will be populated by JS -->
151
+ </div>
152
+ </div>
153
+ </div>
154
+ </div>
155
+ </div>
156
+
157
+ <!-- Showcase Overlay -->
158
+ <div id="showcase-overlay" class="showcase-overlay" style="display: none;">
159
+ <div id="showcase-card" class="showcase-card">
160
+ <button id="showcase-close-btn" class="btn-close-glass">&times;</button>
161
+ <div id="showcase-content" class="showcase-content">
162
+ <!-- Dynamic content will be injected here -->
163
+ </div>
164
+ </div>
165
+ </div>
166
+
167
+ <!-- Bootstrap 5 JS Bundle -->
168
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
169
+ <!-- Custom JS -->
170
+ <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
171
+ <script src="{{ url_for('static', filename='js/main.js') }}"></script>
172
+ </body>
173
+ </html>