Yash goyal commited on
Commit
797b726
·
verified ·
1 Parent(s): 8848da0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +110 -36
app.py CHANGED
@@ -1,4 +1,6 @@
1
  from flask import Flask, render_template, request, redirect, url_for, session, send_file
 
 
2
  import tensorflow as tf
3
  import numpy as np
4
  from PIL import Image
@@ -18,19 +20,27 @@ from flask import jsonify, url_for
18
  app = Flask(__name__)
19
  app.secret_key = "e3f6f40bb8b2471b9f07c4025d845be9"
20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  MODEL_PATH = "skin_lesion_model.h5"
22
  HISTORY_PATH = "training_history.pkl"
23
  PLOT_PATH = "/tmp/static/training_plot.png"
24
  LOGO_PATH = "static/logo.jpg"
25
  IMG_SIZE = (224, 224)
26
  CONFIDENCE_THRESHOLD = 0.30
27
- app.config['MAIL_SERVER'] = 'smtp.gmail.com'
28
- app.config['MAIL_PORT'] = 465
29
- app.config['MAIL_USERNAME'] = os.environ.get('MAIL_USERNAME') # Your Gmail address
30
- app.config['MAIL_PASSWORD'] = os.environ.get('MAIL_PASSWORD') # Your Gmail App Password
31
- app.config['MAIL_USE_TLS'] = False
32
- app.config['MAIL_USE_SSL'] = True
33
- mail = Mail(app)
34
 
35
  label_map = {
36
  0: "Melanoma",
@@ -122,6 +132,24 @@ recommendations = {
122
  logging.basicConfig(level=logging.INFO)
123
  logger = logging.getLogger(__name__)
124
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  # Load Model
126
  try:
127
  logger.info("Loading model from %s", MODEL_PATH)
@@ -171,14 +199,13 @@ def generate_pdf(report, filepath):
171
 
172
  # Logo from root directory - square JPG format
173
  try:
174
- logo_path = "./logo.jpg" # Changed to JPG format
175
  if os.path.exists(logo_path):
176
- # Square logo container - no circular mask since logo is square
177
  c.setFillColor(colors.white)
178
- c.rect(65, y-25, 50, 50, fill=1, stroke=1) # Square container
179
  c.setStrokeColor(colors.Color(0.7, 0.7, 0.7, alpha=1))
180
  c.setLineWidth(1)
181
- c.rect(65, y-25, 50, 50, fill=0, stroke=1) # Square border
182
  c.drawImage(logo_path, 67, y-23, width=46, height=46, preserveAspectRatio=True, mask='auto')
183
  except Exception as e:
184
  logger.warning("Logo error: %s", str(e))
@@ -204,8 +231,6 @@ def generate_pdf(report, filepath):
204
  nonlocal y
205
 
206
  box_height = len(fields) * 20 + 40
207
-
208
- # Main box with subtle shadow
209
  c.setFillColor(colors.Color(0.96, 0.96, 0.96, alpha=0.3))
210
  c.rect(42, y - box_height - 2, width - 84, box_height, fill=1, stroke=0)
211
 
@@ -213,11 +238,9 @@ def generate_pdf(report, filepath):
213
  c.rect(40, y - box_height, width - 80, box_height, fill=1, stroke=1)
214
  c.setStrokeColor(colors.Color(0.9, 0.9, 0.9, alpha=1))
215
 
216
- # Title bar
217
  c.setFillColor(colors.Color(0.95, 0.95, 0.95, alpha=1))
218
  c.rect(40, y - 30, width - 80, 30, fill=1, stroke=0)
219
 
220
- # Title
221
  c.setFont("Helvetica-Bold", 12)
222
  c.setFillColor(colors.Color(0.3, 0.3, 0.3, alpha=1))
223
  c.drawString(55, y - 20, title)
@@ -237,7 +260,6 @@ def generate_pdf(report, filepath):
237
 
238
  y -= extra_gap
239
 
240
- # Sections
241
  professional_section_box("Patient Information", {
242
  "Name": report["name"],
243
  "Email": report["email"],
@@ -265,7 +287,6 @@ def generate_pdf(report, filepath):
265
  f"{i+1}. {line}": "" for i, line in enumerate(treatment["medications"])
266
  })
267
 
268
- # Professional disclaimer
269
  c.setFillColor(colors.Color(0.98, 0.98, 0.98, alpha=1))
270
  c.rect(40, 40, width - 80, 70, fill=1, stroke=1)
271
  c.setStrokeColor(colors.Color(0.9, 0.9, 0.9, alpha=1))
@@ -307,16 +328,13 @@ def api_history():
307
 
308
  user = User.query.filter_by(email=user_email).first()
309
  if not user:
310
- return jsonify([]) # Return empty list if user not found
311
 
312
  scans = Scan.query.filter_by(user_id=user.id).order_by(Scan.timestamp.desc()).all()
313
 
314
  history_data = []
315
  for scan in scans:
316
- # We need to provide the full URL for the image
317
- # In a real deployment, you might need to replace 'localhost' with your actual domain
318
  image_url = url_for('uploaded_file', filename=scan.image_filename, _external=True)
319
-
320
  history_data.append({
321
  "id": scan.id,
322
  "prediction": scan.prediction,
@@ -328,7 +346,6 @@ def api_history():
328
 
329
  return jsonify(history_data)
330
 
331
- # --- New API Endpoint to Email a Report ---
332
  @app.route("/api/email-report/<int:scan_id>")
333
  def email_report(scan_id):
334
  scan = Scan.query.get(scan_id)
@@ -336,17 +353,18 @@ def email_report(scan_id):
336
  return jsonify({"error": "Report not found"}), 404
337
 
338
  try:
339
- # Generate the PDF report
340
  report_data = {
341
- "name": scan.user.name, "email": scan.user.email,
342
- "gender": scan.patient_gender, "age": scan.patient_age,
343
- "prediction": scan.prediction, "confidence": scan.confidence,
 
 
 
344
  "message": ""
345
  }
346
  pdf_path = f"/tmp/report_{scan_id}.pdf"
347
  generate_pdf(report_data, pdf_path)
348
 
349
- # Create and send the email
350
  msg = Message(
351
  'Your SnapSkin Diagnostic Report',
352
  sender=app.config['MAIL_USERNAME'],
@@ -357,8 +375,6 @@ def email_report(scan_id):
357
  msg.attach(f"SnapSkin_Report_{scan_id}.pdf", "application/pdf", fp.read())
358
 
359
  mail.send(msg)
360
-
361
- # Clean up the temporary PDF file
362
  os.remove(pdf_path)
363
 
364
  return jsonify({"success": True, "message": f"Report sent to {scan.user.email}"})
@@ -367,34 +383,86 @@ def email_report(scan_id):
367
  logger.error(f"Failed to send email for scan {scan_id}: {e}")
368
  return jsonify({"success": False, "message": "Failed to send email."}), 500
369
 
370
-
371
  @app.route("/predict", methods=["POST"])
372
  def predict():
373
  try:
374
  if "image" not in request.files:
375
  raise ValueError("No image uploaded.")
376
- image = request.files["image"].read()
377
- img_array = preprocess_image(image)
 
378
  prediction = model.predict(img_array)[0]
379
  predicted_index = int(np.argmax(prediction))
380
  confidence = float(prediction[predicted_index])
381
  label = label_map.get(predicted_index, "Unknown") if confidence >= CONFIDENCE_THRESHOLD else "Low confidence"
382
  msg = "⚠ This image is not confidently recognized. Please upload a clearer image." if confidence < CONFIDENCE_THRESHOLD else ""
383
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
384
  report = {
385
  "name": request.form.get("name"),
386
- "email": request.form.get("email"),
387
  "gender": request.form.get("gender"),
388
  "age": request.form.get("age"),
389
  "prediction": label,
390
  "confidence": f"{confidence * 100:.2f}%",
391
- "message": msg
 
392
  }
393
  session["report"] = report
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
394
  return redirect(url_for("result"))
395
  except Exception as e:
396
  return render_template("form.html", history_plot="/training_plot.png", result={
397
- "prediction": "Error", "confidence": "N/A", "message": str(e)
 
 
 
398
  })
399
 
400
  @app.route("/result")
@@ -413,5 +481,11 @@ def download_report():
413
  generate_pdf(report, filepath)
414
  return send_file(filepath, as_attachment=True)
415
 
 
 
 
 
416
  if __name__ == "__main__":
417
- app.run(host="0.0.0.0", port=7860)
 
 
 
1
  from flask import Flask, render_template, request, redirect, url_for, session, send_file
2
+ from flask_sqlalchemy import SQLAlchemy
3
+ from flask_migrate import Migrate
4
  import tensorflow as tf
5
  import numpy as np
6
  from PIL import Image
 
20
  app = Flask(__name__)
21
  app.secret_key = "e3f6f40bb8b2471b9f07c4025d845be9"
22
 
23
+ # Database configuration
24
+ app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///snapsin.db'
25
+ app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
26
+ db = SQLAlchemy(app)
27
+ migrate = Migrate(app, db)
28
+
29
+ # Mail configuration
30
+ app.config['MAIL_SERVER'] = 'smtp.gmail.com'
31
+ app.config['MAIL_PORT'] = 465
32
+ app.config['MAIL_USERNAME'] = os.environ.get('MAIL_USERNAME')
33
+ app.config['MAIL_PASSWORD'] = os.environ.get('MAIL_PASSWORD')
34
+ app.config['MAIL_USE_TLS'] = False
35
+ app.config['MAIL_USE_SSL'] = True
36
+ mail = Mail(app)
37
+
38
  MODEL_PATH = "skin_lesion_model.h5"
39
  HISTORY_PATH = "training_history.pkl"
40
  PLOT_PATH = "/tmp/static/training_plot.png"
41
  LOGO_PATH = "static/logo.jpg"
42
  IMG_SIZE = (224, 224)
43
  CONFIDENCE_THRESHOLD = 0.30
 
 
 
 
 
 
 
44
 
45
  label_map = {
46
  0: "Melanoma",
 
132
  logging.basicConfig(level=logging.INFO)
133
  logger = logging.getLogger(__name__)
134
 
135
+ # Database Models
136
+ class User(db.Model):
137
+ id = db.Column(db.Integer, primary_key=True)
138
+ name = db.Column(db.String(100), nullable=False)
139
+ email = db.Column(db.String(120), unique=True, nullable=False)
140
+ scans = db.relationship('Scan', backref='user', lazy=True)
141
+
142
+ class Scan(db.Model):
143
+ id = db.Column(db.Integer, primary_key=True)
144
+ user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
145
+ patient_name = db.Column(db.String(100), nullable=False)
146
+ patient_gender = db.Column(db.String(20), nullable=False)
147
+ patient_age = db.Column(db.Integer, nullable=False)
148
+ prediction = db.Column(db.String(100), nullable=False)
149
+ confidence = db.Column(db.String(20), nullable=False)
150
+ timestamp = db.Column(db.DateTime, default=datetime.utcnow)
151
+ image_filename = db.Column(db.String(100), nullable=False)
152
+
153
  # Load Model
154
  try:
155
  logger.info("Loading model from %s", MODEL_PATH)
 
199
 
200
  # Logo from root directory - square JPG format
201
  try:
202
+ logo_path = "./logo.jpg"
203
  if os.path.exists(logo_path):
 
204
  c.setFillColor(colors.white)
205
+ c.rect(65, y-25, 50, 50, fill=1, stroke=1)
206
  c.setStrokeColor(colors.Color(0.7, 0.7, 0.7, alpha=1))
207
  c.setLineWidth(1)
208
+ c.rect(65, y-25, 50, 50, fill=0, stroke=1)
209
  c.drawImage(logo_path, 67, y-23, width=46, height=46, preserveAspectRatio=True, mask='auto')
210
  except Exception as e:
211
  logger.warning("Logo error: %s", str(e))
 
231
  nonlocal y
232
 
233
  box_height = len(fields) * 20 + 40
 
 
234
  c.setFillColor(colors.Color(0.96, 0.96, 0.96, alpha=0.3))
235
  c.rect(42, y - box_height - 2, width - 84, box_height, fill=1, stroke=0)
236
 
 
238
  c.rect(40, y - box_height, width - 80, box_height, fill=1, stroke=1)
239
  c.setStrokeColor(colors.Color(0.9, 0.9, 0.9, alpha=1))
240
 
 
241
  c.setFillColor(colors.Color(0.95, 0.95, 0.95, alpha=1))
242
  c.rect(40, y - 30, width - 80, 30, fill=1, stroke=0)
243
 
 
244
  c.setFont("Helvetica-Bold", 12)
245
  c.setFillColor(colors.Color(0.3, 0.3, 0.3, alpha=1))
246
  c.drawString(55, y - 20, title)
 
260
 
261
  y -= extra_gap
262
 
 
263
  professional_section_box("Patient Information", {
264
  "Name": report["name"],
265
  "Email": report["email"],
 
287
  f"{i+1}. {line}": "" for i, line in enumerate(treatment["medications"])
288
  })
289
 
 
290
  c.setFillColor(colors.Color(0.98, 0.98, 0.98, alpha=1))
291
  c.rect(40, 40, width - 80, 70, fill=1, stroke=1)
292
  c.setStrokeColor(colors.Color(0.9, 0.9, 0.9, alpha=1))
 
328
 
329
  user = User.query.filter_by(email=user_email).first()
330
  if not user:
331
+ return jsonify([])
332
 
333
  scans = Scan.query.filter_by(user_id=user.id).order_by(Scan.timestamp.desc()).all()
334
 
335
  history_data = []
336
  for scan in scans:
 
 
337
  image_url = url_for('uploaded_file', filename=scan.image_filename, _external=True)
 
338
  history_data.append({
339
  "id": scan.id,
340
  "prediction": scan.prediction,
 
346
 
347
  return jsonify(history_data)
348
 
 
349
  @app.route("/api/email-report/<int:scan_id>")
350
  def email_report(scan_id):
351
  scan = Scan.query.get(scan_id)
 
353
  return jsonify({"error": "Report not found"}), 404
354
 
355
  try:
 
356
  report_data = {
357
+ "name": scan.user.name,
358
+ "email": scan.user.email,
359
+ "gender": scan.patient_gender,
360
+ "age": scan.patient_age,
361
+ "prediction": scan.prediction,
362
+ "confidence": scan.confidence,
363
  "message": ""
364
  }
365
  pdf_path = f"/tmp/report_{scan_id}.pdf"
366
  generate_pdf(report_data, pdf_path)
367
 
 
368
  msg = Message(
369
  'Your SnapSkin Diagnostic Report',
370
  sender=app.config['MAIL_USERNAME'],
 
375
  msg.attach(f"SnapSkin_Report_{scan_id}.pdf", "application/pdf", fp.read())
376
 
377
  mail.send(msg)
 
 
378
  os.remove(pdf_path)
379
 
380
  return jsonify({"success": True, "message": f"Report sent to {scan.user.email}"})
 
383
  logger.error(f"Failed to send email for scan {scan_id}: {e}")
384
  return jsonify({"success": False, "message": "Failed to send email."}), 500
385
 
 
386
  @app.route("/predict", methods=["POST"])
387
  def predict():
388
  try:
389
  if "image" not in request.files:
390
  raise ValueError("No image uploaded.")
391
+ image = request.files["image"]
392
+ image_bytes = image.read()
393
+ img_array = preprocess_image(image_bytes)
394
  prediction = model.predict(img_array)[0]
395
  predicted_index = int(np.argmax(prediction))
396
  confidence = float(prediction[predicted_index])
397
  label = label_map.get(predicted_index, "Unknown") if confidence >= CONFIDENCE_THRESHOLD else "Low confidence"
398
  msg = "⚠ This image is not confidently recognized. Please upload a clearer image." if confidence < CONFIDENCE_THRESHOLD else ""
399
 
400
+ # Save user and scan data
401
+ email = request.form.get("email")
402
+ user = User.query.filter_by(email=email).first()
403
+ if not user:
404
+ user = User(name=request.form.get("name"), email=email)
405
+ db.session.add(user)
406
+ db.session.commit()
407
+
408
+ # Save image
409
+ timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
410
+ image_filename = f"scan_{timestamp}.jpg"
411
+ image_path = os.path.join("static/uploads", image_filename)
412
+ os.makedirs("static/uploads", exist_ok=True)
413
+ image.seek(0)
414
+ image.save(image_path)
415
+
416
+ scan = Scan(
417
+ user_id=user.id,
418
+ patient_name=request.form.get("name"),
419
+ patient_gender=request.form.get("gender"),
420
+ patient_age=int(request.form.get("age")),
421
+ prediction=label,
422
+ confidence=f"{confidence * 100:.2f}%",
423
+ image_filename=image_filename
424
+ )
425
+ db.session.add(scan)
426
+ db.session.commit()
427
+
428
  report = {
429
  "name": request.form.get("name"),
430
+ "email": email,
431
  "gender": request.form.get("gender"),
432
  "age": request.form.get("age"),
433
  "prediction": label,
434
  "confidence": f"{confidence * 100:.2f}%",
435
+ "message": msg,
436
+ "scan_id": scan.id
437
  }
438
  session["report"] = report
439
+
440
+ # Send email automatically
441
+ try:
442
+ pdf_path = f"/tmp/report_{scan.id}.pdf"
443
+ generate_pdf(report, pdf_path)
444
+ msg = Message(
445
+ 'Your SnapSkin Diagnostic Report',
446
+ sender=app.config['MAIL_USERNAME'],
447
+ recipients=[email]
448
+ )
449
+ msg.body = f"Dear {report['name']},\n\nPlease find your diagnostic report attached.\n\nThank you for using SnapSkin."
450
+ with app.open_resource(pdf_path) as fp:
451
+ msg.attach(f"SnapSkin_Report_{scan.id}.pdf", "application/pdf", fp.read())
452
+ mail.send(msg)
453
+ os.remove(pdf_path)
454
+ report["email_status"] = "Report sent to your email."
455
+ except Exception as e:
456
+ logger.error(f"Failed to send email: {e}")
457
+ report["email_status"] = "Failed to send report to email."
458
+
459
  return redirect(url_for("result"))
460
  except Exception as e:
461
  return render_template("form.html", history_plot="/training_plot.png", result={
462
+ "prediction": "Error",
463
+ "confidence": "N/A",
464
+ "message": str(e),
465
+ "email_status": "Error occurred, no email sent."
466
  })
467
 
468
  @app.route("/result")
 
481
  generate_pdf(report, filepath)
482
  return send_file(filepath, as_attachment=True)
483
 
484
+ @app.route("/uploads/<filename>")
485
+ def uploaded_file(filename):
486
+ return send_file(os.path.join("static/uploads", filename))
487
+
488
  if __name__ == "__main__":
489
+ with app.app_context():
490
+ db.create_all()
491
+ app.run(host="0.0.0.0", port=7860)