File size: 24,012 Bytes
b5f303c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
import os
import numpy as np
import tensorflow as tf
from flask import Flask, render_template, request, jsonify, redirect, url_for, flash, send_from_directory, send_file
from werkzeug.utils import secure_filename
from PIL import Image
import io
import base64
from datetime import datetime
import tempfile

# PDF generation dependencies with error handling
try:
    from reportlab.lib.pagesizes import letter, A4
    from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Image as RLImage, Table, TableStyle
    from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
    from reportlab.lib.units import inch
    from reportlab.lib import colors
    from reportlab.lib.enums import TA_CENTER, TA_LEFT
    REPORTLAB_AVAILABLE = True
    print("βœ… ReportLab library loaded successfully!")
except ImportError as e:
    REPORTLAB_AVAILABLE = False
    print(f"⚠️ ReportLab not available: {e}")
    print("πŸ“ PDF generation will use client-side fallback only")

app = Flask(__name__)
app.secret_key = 'your-secret-key-here'  # Change this to a secure secret key

# Configuration
UPLOAD_FOLDER = 'static/uploads'
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
MAX_CONTENT_LENGTH = 16 * 1024 * 1024  # 16MB max file size

app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = MAX_CONTENT_LENGTH

# Create upload directory if it doesn't exist
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
os.makedirs('static/css', exist_ok=True)
os.makedirs('static/js', exist_ok=True)
os.makedirs('templates', exist_ok=True)

@app.route('/uploads/<filename>')
def uploaded_file(filename):
    """Serve uploaded files"""
    return send_from_directory(app.config['UPLOAD_FOLDER'], filename)

# Load the trained model
MODEL_PATH = "models/1.h5"  # Update this path if needed
try:
    model = tf.keras.models.load_model(MODEL_PATH)
    print("βœ… Model loaded successfully!")
    MODEL_LOADED = True
except Exception as e:
    print(f"❌ Error loading model: {e}")
    MODEL_LOADED = False
    model = None

# Class names from your training (must match the exact order from training)
# Training order: ['Potato___Early_blight', 'Potato___Late_blight', 'Potato___healthy']
CLASS_NAMES = ["Potato___Early_blight", "Potato___Late_blight", "Potato___healthy"]
CLASS_DISPLAY_NAMES = ["Early Blight", "Late Blight", "Healthy"]
CLASS_DESCRIPTIONS = {
    "Potato___Early_blight": "A common fungal disease that causes dark spots on potato leaves. Treatment with copper-based fungicides is recommended.",
    "Potato___Late_blight": "A serious disease caused by Phytophthora infestans. Immediate action required - remove infected plants and apply appropriate fungicides.",
    "Potato___healthy": "The potato plant appears healthy with no signs of disease detected. Continue good agricultural practices."
}

# Image preprocessing parameters
IMAGE_SIZE = 256

def allowed_file(filename):
    """Check if file extension is allowed"""
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

def preprocess_image(image):
    """Preprocess the uploaded image for prediction"""
    try:
        # Convert to RGB if necessary
        if image.mode != "RGB":
            image = image.convert("RGB")
        
        # Resize image
        image = image.resize((IMAGE_SIZE, IMAGE_SIZE))
        
        # Convert to numpy array 
        img_array = np.array(image)
        
        # DO NOT normalize here - the model has built-in rescaling layer
        # The model expects pixel values in range [0, 255]
        # img_array = img_array / 255.0  # Removed this line
        
        # Add batch dimension
        img_array = np.expand_dims(img_array, axis=0)
        
        # Debug: Print image statistics
        print(f"Image shape: {img_array.shape}")
        print(f"Image pixel range: [{img_array.min():.2f}, {img_array.max():.2f}]")
        
        return img_array
    except Exception as e:
        print(f"Error preprocessing image: {e}")
        return None

def predict_disease(image):
    """Make prediction on the preprocessed image"""
    if not MODEL_LOADED or model is None:
        return {"error": "Model not loaded"}
    
    try:
        # Preprocess image
        processed_image = preprocess_image(image)
        if processed_image is None:
            return {"error": "Failed to preprocess image"}
        
        # Make prediction
        predictions = model.predict(processed_image)
        predicted_class_index = np.argmax(predictions[0])
        confidence = float(np.max(predictions[0]))
        
        # Debug: Print prediction details
        print(f"Raw predictions: {predictions[0]}")
        print(f"Predicted class index: {predicted_class_index}")
        print(f"Confidence: {confidence:.4f}")
        
        # Get class name
        predicted_class = CLASS_NAMES[predicted_class_index]
        predicted_display_name = CLASS_DISPLAY_NAMES[predicted_class_index]
        
        # Create detailed results
        all_predictions = {}
        for i, (class_name, display_name) in enumerate(zip(CLASS_NAMES, CLASS_DISPLAY_NAMES)):
            all_predictions[display_name] = {
                'probability': round(float(predictions[0][i]) * 100, 2),
                'description': CLASS_DESCRIPTIONS[class_name]
            }
        
        return {
            "predicted_class": predicted_display_name,
            "confidence": round(confidence * 100, 2),
            "description": CLASS_DESCRIPTIONS[predicted_class],
            "all_predictions": all_predictions,
            "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        }
        
    except Exception as e:
        print(f"Prediction error: {e}")
        return {"error": f"Prediction failed: {str(e)}"}

def get_recommendations(disease_name, confidence):
    """Get treatment recommendations based on prediction"""
    recommendations = {
        'Early Blight': [
            "Remove affected leaves immediately and dispose properly",
            "Apply copper-based fungicide spray",
            "Improve air circulation around plants",
            "Avoid overhead watering",
            "Consider crop rotation for next season"
        ],
        'Late Blight': [
            "URGENT: Remove and destroy infected plants immediately",
            "Apply systemic fungicides (metalaxyl-based)",
            "Monitor weather conditions closely",
            "Increase plant spacing for better air circulation",
            "Harvest healthy tubers as soon as possible"
        ],
        'Healthy': [
            "Continue current care practices",
            "Maintain proper watering schedule",
            "Monitor plants regularly for early signs of disease",
            "Ensure good soil drainage",
            "Apply balanced fertilizer as needed"
        ]
    }
    return recommendations.get(disease_name, ["Consult agricultural expert for specific advice"])

@app.route('/')
def index():
    """Main page"""
    return render_template('index.html', model_loaded=MODEL_LOADED)

@app.route('/test')
def test_upload():
    """Simple upload test page"""
    return render_template('test_upload.html')

@app.route('/debug')
def debug_upload():
    """Debug upload test page"""
    return render_template('debug_upload.html')

@app.route('/predict', methods=['POST'])
def predict():
    """Handle image upload and prediction"""
    try:
        print(f"Received prediction request. Files: {list(request.files.keys())}")
        
        if 'file' not in request.files:
            print("No file in request")
            return jsonify({'error': 'No file uploaded'}), 400
        
        file = request.files['file']
        print(f"File received: {file.filename}, size: {file.content_length if hasattr(file, 'content_length') else 'unknown'}")
        
        if file.filename == '':
            print("Empty filename")
            return jsonify({'error': 'No file selected'}), 400
        
        if not allowed_file(file.filename):
            print(f"Invalid file type: {file.filename}")
            return jsonify({'error': 'Invalid file type. Please upload PNG, JPG, or JPEG files.'}), 400
        
        # Ensure upload directory exists
        os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
        
        # Save uploaded file
        filename = secure_filename(file.filename)
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        filename = f"{timestamp}_{filename}"
        filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
        
        print(f"Saving file to: {filepath}")
        file.save(filepath)
        print(f"File saved successfully")
        
        # Verify file exists
        if not os.path.exists(filepath):
            print(f"File was not saved properly: {filepath}")
            return jsonify({'error': 'Failed to save uploaded file'}), 500
        
        print(f"File size on disk: {os.path.getsize(filepath)} bytes")
        
        # Open and predict
        try:
            image = Image.open(filepath)
            print(f"Image opened successfully: {image.size}, mode: {image.mode}")
        except Exception as e:
            print(f"Failed to open image: {e}")
            return jsonify({'error': f'Invalid image file: {str(e)}'}), 400
        
        result = predict_disease(image)
        print(f"Prediction result: {result}")
        
        if 'error' in result:
            return jsonify(result), 500
        
        # Add recommendations and image URL for upload method
        result['recommendations'] = get_recommendations(result['predicted_class'], result['confidence'])
        result['image_url'] = url_for('uploaded_file', filename=filename)
        
        print(f"Final result with image URL: {result['image_url']}")
        return jsonify(result)
        
    except Exception as e:
        print(f"Prediction error: {e}")
        import traceback
        traceback.print_exc()
        return jsonify({'error': f'Prediction failed: {str(e)}'}), 500

@app.route('/predict_camera', methods=['POST'])
def predict_camera():
    """Handle camera image prediction"""
    try:
        data = request.get_json()
        
        if 'image' not in data:
            return jsonify({'error': 'No image data provided'}), 400
        
        # Decode base64 image
        image_data = data['image'].split(',')[1]  # Remove data:image/png;base64, prefix
        image_bytes = base64.b64decode(image_data)
        image = Image.open(io.BytesIO(image_bytes))
        
        # Save camera image
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        filename = f"camera_{timestamp}.png"
        filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
        image.save(filepath)
        
        # Make prediction
        result = predict_disease(image)
        
        if 'error' in result:
            return jsonify(result), 500
        
        # Add recommendations and image URL for camera method
        result['recommendations'] = get_recommendations(result['predicted_class'], result['confidence'])
        result['image_url'] = url_for('uploaded_file', filename=filename)
        
        return jsonify(result)
        
    except Exception as e:
        return jsonify({'error': f'Camera prediction failed: {str(e)}'}), 500

@app.route('/health')
def health():
    """Health check endpoint"""
    upload_dir_exists = os.path.exists(app.config['UPLOAD_FOLDER'])
    upload_dir_writable = os.access(app.config['UPLOAD_FOLDER'], os.W_OK) if upload_dir_exists else False
    
    return jsonify({
        'status': 'healthy',
        'model_loaded': MODEL_LOADED,
        'upload_dir_exists': upload_dir_exists,
        'upload_dir_writable': upload_dir_writable,
        'upload_path': app.config['UPLOAD_FOLDER'],
        'timestamp': datetime.now().isoformat()
    })

@app.route('/debug/upload-test')
def debug_upload_test():
    """Debug endpoint to test upload directory"""
    try:
        # Ensure upload directory exists
        os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
        
        # Test file creation
        test_file = os.path.join(app.config['UPLOAD_FOLDER'], 'test.txt')
        with open(test_file, 'w') as f:
            f.write('test')
        
        # Clean up test file
        os.remove(test_file)
        
        return jsonify({
            'status': 'success',
            'message': 'Upload directory is working correctly',
            'path': app.config['UPLOAD_FOLDER']
        })
    except Exception as e:
        return jsonify({
            'status': 'error',
            'message': f'Upload directory test failed: {str(e)}',
            'path': app.config['UPLOAD_FOLDER']
        }), 500

def test_model_predictions():
    """Test the model with some dummy data to verify it's working correctly"""
    if not MODEL_LOADED or model is None:
        return {"error": "Model not loaded"}
    
    try:
        # Create dummy test data - same shape as expected input
        dummy_image = np.random.randint(0, 255, (1, IMAGE_SIZE, IMAGE_SIZE, 3), dtype=np.uint8)
        
        # Make prediction
        predictions = model.predict(dummy_image)
        
        print(f"Model test - Input shape: {dummy_image.shape}")
        print(f"Model test - Output shape: {predictions.shape}")
        print(f"Model test - Predictions: {predictions[0]}")
        print(f"Model test - Sum of predictions: {np.sum(predictions[0])}")
        print(f"Model test - Class names order: {CLASS_NAMES}")
        
        return {
            "status": "success",
            "input_shape": str(dummy_image.shape),
            "output_shape": str(predictions.shape),
            "predictions": predictions[0].tolist(),
            "prediction_sum": float(np.sum(predictions[0])),
            "class_names": CLASS_NAMES
        }
    except Exception as e:
        print(f"Model test error: {e}")
        return {"error": f"Model test failed: {str(e)}"}

@app.route('/debug/model-test')
def debug_model_test():
    """Debug endpoint to test model functionality"""
    result = test_model_predictions()
    return jsonify(result)

@app.errorhandler(413)
def too_large(e):
    return jsonify({'error': 'File too large. Maximum size is 16MB.'}), 413

@app.errorhandler(404)
def not_found(e):
    return render_template('index.html', model_loaded=MODEL_LOADED)

def generate_pdf_report(prediction_data, image_path=None):
    """Generate a professional PDF report for the disease prediction"""
    if not REPORTLAB_AVAILABLE:
        print("❌ ReportLab not available - cannot generate server-side PDF")
        return None
        
    try:
        # Create a temporary file for the PDF
        temp_pdf = tempfile.NamedTemporaryFile(delete=False, suffix='.pdf')
        
        # Create the PDF document
        doc = SimpleDocTemplate(temp_pdf.name, pagesize=A4)
        styles = getSampleStyleSheet()
        story = []
        
        # Custom styles
        title_style = ParagraphStyle(
            'CustomTitle',
            parent=styles['Title'],
            fontSize=24,
            spaceAfter=30,
            alignment=TA_CENTER,
            textColor=colors.darkgreen
        )
        
        heading_style = ParagraphStyle(
            'CustomHeading',
            parent=styles['Heading2'],
            fontSize=16,
            spaceAfter=12,
            textColor=colors.darkblue
        )
        
        # Title
        story.append(Paragraph("πŸ₯” POTATO DISEASE DETECTION REPORT", title_style))
        story.append(Spacer(1, 20))
        
        # Header info table
        header_data = [
            ['Report Generated:', prediction_data.get('timestamp', datetime.now().strftime("%Y-%m-%d %H:%M:%S"))],
            ['Analysis Method:', 'Deep Learning AI Classification'],
            ['Model Version:', 'TensorFlow/Keras CNN v1.0']
        ]
        
        header_table = Table(header_data, colWidths=[2*inch, 4*inch])
        header_table.setStyle(TableStyle([
            ('BACKGROUND', (0, 0), (0, -1), colors.lightgrey),
            ('TEXTCOLOR', (0, 0), (-1, -1), colors.black),
            ('ALIGN', (0, 0), (-1, -1), 'LEFT'),
            ('FONTNAME', (0, 0), (-1, -1), 'Helvetica-Bold'),
            ('FONTSIZE', (0, 0), (-1, -1), 10),
            ('BOTTOMPADDING', (0, 0), (-1, -1), 12),
            ('GRID', (0, 0), (-1, -1), 1, colors.black)
        ]))
        
        story.append(header_table)
        story.append(Spacer(1, 30))
        
        # Add image if provided
        if image_path and os.path.exists(image_path):
            story.append(Paragraph("πŸ“Έ ANALYZED IMAGE", heading_style))
            try:
                # Resize image to fit in PDF
                img = RLImage(image_path)
                img.drawHeight = 3 * inch
                img.drawWidth = 3 * inch
                story.append(img)
                story.append(Spacer(1, 20))
            except Exception as img_error:
                print(f"Warning: Could not add image to PDF: {img_error}")
                story.append(Paragraph("Image could not be embedded in PDF", styles['Normal']))
                story.append(Spacer(1, 20))
        
        # Diagnosis Section
        story.append(Paragraph("🎯 DIAGNOSIS RESULTS", heading_style))
        
        # Main diagnosis
        diagnosis_data = [
            ['Predicted Disease:', prediction_data.get('predicted_class', 'Unknown')],
            ['Confidence Level:', f"{prediction_data.get('confidence', 0):.2f}%"],
            ['Risk Assessment:', get_risk_level(prediction_data.get('confidence', 0))]
        ]
        
        diagnosis_table = Table(diagnosis_data, colWidths=[2*inch, 4*inch])
        diagnosis_table.setStyle(TableStyle([
            ('BACKGROUND', (0, 0), (0, -1), colors.lightblue),
            ('TEXTCOLOR', (0, 0), (-1, -1), colors.black),
            ('ALIGN', (0, 0), (-1, -1), 'LEFT'),
            ('FONTNAME', (0, 0), (-1, -1), 'Helvetica'),
            ('FONTSIZE', (0, 0), (-1, -1), 12),
            ('BOTTOMPADDING', (0, 0), (-1, -1), 12),
            ('GRID', (0, 0), (-1, -1), 1, colors.black)
        ]))
        
        story.append(diagnosis_table)
        story.append(Spacer(1, 20))
        
        # Description
        story.append(Paragraph("πŸ“‹ DESCRIPTION", heading_style))
        description = Paragraph(prediction_data.get('description', 'No description available.'), styles['Normal'])
        story.append(description)
        story.append(Spacer(1, 20))
        
        # Probability breakdown
        story.append(Paragraph("πŸ“Š PROBABILITY BREAKDOWN", heading_style))
        
        prob_data = [['Disease Type', 'Probability']]
        all_predictions = prediction_data.get('all_predictions', {})
        for disease, info in all_predictions.items():
            prob_data.append([disease, f"{info.get('probability', 0):.2f}%"])
        
        prob_table = Table(prob_data, colWidths=[3*inch, 2*inch])
        prob_table.setStyle(TableStyle([
            ('BACKGROUND', (0, 0), (-1, 0), colors.grey),
            ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
            ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
            ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
            ('FONTNAME', (0, 1), (-1, -1), 'Helvetica'),
            ('FONTSIZE', (0, 0), (-1, -1), 10),
            ('BOTTOMPADDING', (0, 0), (-1, -1), 12),
            ('GRID', (0, 0), (-1, -1), 1, colors.black)
        ]))
        
        story.append(prob_table)
        story.append(Spacer(1, 20))
        
        # Recommendations
        story.append(Paragraph("πŸ’‘ TREATMENT RECOMMENDATIONS", heading_style))
        
        recommendations = prediction_data.get('recommendations', [])
        for i, rec in enumerate(recommendations, 1):
            rec_text = f"{i}. {rec}"
            story.append(Paragraph(rec_text, styles['Normal']))
            story.append(Spacer(1, 8))
        
        story.append(Spacer(1, 30))
        
        # Footer
        footer_style = ParagraphStyle(
            'Footer',
            parent=styles['Normal'],
            fontSize=10,
            alignment=TA_CENTER,
            textColor=colors.grey
        )
        
        story.append(Paragraph("_______________________________________________", footer_style))
        story.append(Spacer(1, 10))
        story.append(Paragraph("Generated by Potato Disease Detection System", footer_style))
        story.append(Paragraph("Powered by Flask & TensorFlow | Lucky Sharma", footer_style))
        story.append(Paragraph("Β© 2025 All Rights Reserved", footer_style))
        
        # Build PDF
        doc.build(story)
        
        print(f"βœ… PDF report generated successfully: {temp_pdf.name}")
        return temp_pdf.name
        
    except Exception as e:
        print(f"❌ Error generating PDF: {e}")
        import traceback
        traceback.print_exc()
        return None

def get_risk_level(confidence):
    """Determine risk level based on confidence"""
    if confidence >= 80:
        return "High Confidence"
    elif confidence >= 60:
        return "Medium Confidence"
    else:
        return "Low Confidence - Manual Verification Recommended"

@app.route('/generate-pdf-report', methods=['POST'])
def generate_pdf_report_route():
    """Generate and download PDF report"""
    try:
        data = request.get_json()
        
        if not data:
            return jsonify({'error': 'No data provided'}), 400
        
        # Check if ReportLab is available
        if not REPORTLAB_AVAILABLE:
            print("⚠️ ReportLab not available, suggesting client-side fallback")
            return jsonify({
                'error': 'Server-side PDF generation not available',
                'fallback': 'client',
                'message': 'ReportLab library not installed. Using client-side fallback.'
            }), 503
        
        # Get image path if provided
        image_path = None
        if 'image_url' in data:
            # Extract filename from URL and construct full path
            image_filename = data['image_url'].split('/')[-1]
            image_path = os.path.join(app.config['UPLOAD_FOLDER'], image_filename)
            
            # Verify image exists
            if not os.path.exists(image_path):
                image_path = None
        
        # Generate PDF
        pdf_path = generate_pdf_report(data, image_path)
        
        if not pdf_path:
            print("❌ PDF generation failed, suggesting client-side fallback")
            return jsonify({
                'error': 'Server-side PDF generation failed',
                'fallback': 'client',
                'message': 'Could not generate PDF on server. Using client-side fallback.'
            }), 503
        
        # Create filename
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        disease_name = data.get('predicted_class', 'unknown').replace(' ', '_')
        pdf_filename = f"potato_disease_report_{disease_name}_{timestamp}.pdf"
        
        print(f"βœ… PDF generated successfully: {pdf_filename}")
        return send_file(
            pdf_path,
            as_attachment=True,
            download_name=pdf_filename,
            mimetype='application/pdf'
        )
        
    except Exception as e:
        print(f"❌ PDF generation error: {e}")
        import traceback
        traceback.print_exc()
        return jsonify({
            'error': f'PDF generation failed: {str(e)}',
            'fallback': 'client',
            'message': 'Server error occurred. Using client-side fallback.'
        }), 503

if __name__ == '__main__':
    print("πŸš€ Starting Potato Disease Detection Flask App...")
    print(f"πŸ“ Upload folder: {UPLOAD_FOLDER}")
    print(f"πŸ€– Model loaded: {MODEL_LOADED}")
    print("🌐 Access the app at: http://localhost:5000")
    print("πŸ’‘ Press Ctrl+C to stop the server")
    
    app.run(debug=True, host='0.0.0.0', port=5000)