from flask import Flask, request, jsonify, send_from_directory import tensorflow as tf import numpy as np import json from tensorflow.keras.preprocessing import image from PIL import Image import io import os import smtplib from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from datetime import datetime from flask_cors import CORS app = Flask(__name__, static_folder='.', static_url_path='') CORS(app) # ───────────────────────────────────────────── # Paths (relative — works on HuggingFace) # ───────────────────────────────────────────── WEIGHTS_PATH = "best_weights.weights.h5" ARCH_PATH = "model_architecture.json" CLASS_NAMES_PATH = "class_names.json" SYMPTOMS_PATH = "symptoms.json" MEDICINES_PATH = "medicines.json" IMG_SIZE = (224, 224) PORT = 7860 # ───────────────────────────────────────────── # Email Configuration # ───────────────────────────────────────────── SENDER_EMAIL = "angrajkarn2004@gmail.com" SENDER_PASSWORD = "wpjh gfuv ipma ibyi" # ───────────────────────────────────────────── # Load Model & Data # ───────────────────────────────────────────── print("Loading model architecture from JSON...") with open(ARCH_PATH, 'r', encoding='utf-8') as f: model_json = f.read() model = tf.keras.models.model_from_json(model_json) print("Loading trained weights...") model.load_weights(WEIGHTS_PATH) with open(CLASS_NAMES_PATH, 'r', encoding='utf-8') as f: class_names = json.load(f) with open(SYMPTOMS_PATH, 'r', encoding='utf-8') as f: DISEASE_SYMPTOMS = json.load(f) with open(MEDICINES_PATH, 'r', encoding='utf-8') as f: MEDICINES_DB = json.load(f) print(f"Model and data loaded! {len(class_names)} classes ready.") # ───────────────────────────────────────────── # Image Preprocessing (identical to local backend) # ───────────────────────────────────────────── def preprocess_image(img_bytes): img = Image.open(io.BytesIO(img_bytes)).convert('RGB').resize(IMG_SIZE) img_array = image.img_to_array(img) img_array = np.expand_dims(img_array, axis=0) img_array = (img_array / 127.5) - 1.0 # ← exact same as local backend return img_array # ───────────────────────────────────────────── # Medicine Lookup # ───────────────────────────────────────────── def get_medicine_info(disease_name): return MEDICINES_DB.get(disease_name, {}) # ───────────────────────────────────────────── # Medicine HTML Block (for email) # ───────────────────────────────────────────── def build_medicine_html_block(meds): if not meds: return "

No specific medication data available.

" SECTION_LABELS = { "topical": "Topical Treatment", "oral_moderate": "Oral Treatment (Moderate)", "oral_severe": "Oral Treatment (Severe)", "systemic": "Systemic Treatment", "first_line": "First-Line Treatment", "adjuvants": "Adjuvant Therapy", "antipruritic": "Antipruritic (Itch Relief)", "antihistamines": "Antihistamines", "emollients": "Emollients / Moisturizers", "surgical": "Surgical Treatment", "immunotherapy": "Immunotherapy", "targeted_therapy": "Targeted Therapy", "biologics_psoriasis": "Biologic Therapy", "systemic_psoriasis": "Systemic (Psoriasis)", "topical_psoriasis": "Topical (Psoriasis)", "lichen_planus": "Lichen Planus Treatment", "rosacea_specific": "Rosacea-Specific", "vitiligo": "Vitiligo Treatment", "melasma": "Melasma Treatment", "photodermatoses": "Photodermatosis Treatment", "actinic_keratosis": "Actinic Keratosis", "basal_cell_carcinoma": "Basal Cell Carcinoma", "squamous_cell_carcinoma": "Squamous Cell Carcinoma", "impetigo_topical": "Impetigo (Topical)", "cellulitis_oral": "Cellulitis (Oral)", "severe_iv": "Severe / IV Therapy", "scabies_first_line": "Scabies First-Line", "scabies_adjuncts": "Scabies Adjuncts", "lyme_disease": "Lyme Disease", "insect_bites": "Insect Bite Relief", "onychomycosis_topical": "Nail Fungus (Topical)", "onychomycosis_oral": "Nail Fungus (Oral)", "nail_psoriasis": "Nail Psoriasis", "tinea_topical": "Tinea (Topical)", "tinea_oral": "Tinea (Oral)", "candidiasis": "Candidiasis", "acute_urticaria": "Acute Urticaria", "chronic_urticaria": "Chronic Urticaria", "anaphylaxis_emergency": "Anaphylaxis Emergency", "herpes_simplex": "Herpes Simplex", "herpes_zoster": "Herpes Zoster", "hpv_warts": "HPV / Warts", "androgenetic_alopecia": "Androgenetic Alopecia", "alopecia_areata": "Alopecia Areata", "telogen_effluvium": "Telogen Effluvium", "tinea_capitis": "Tinea Capitis", "mild": "Mild Cases", "moderate_to_severe": "Moderate-Severe Cases", "allergic_contact_dermatitis": "Allergic Contact Dermatitis", "mild_to_moderate": "Mild to Moderate", "sjs_ten_emergency": "SJS / TEN Emergency", "infantile_hemangioma": "Infantile Hemangioma", "pyogenic_granuloma": "Pyogenic Granuloma", "port_wine_stain": "Port Wine Stain", "cherry_angioma": "Cherry Angioma", "cutaneous_small_vessel": "Cutaneous Vasculitis", "systemic_vasculitis": "Systemic Vasculitis", "cutaneous_lupus": "Cutaneous Lupus", "systemic_lupus": "Systemic Lupus (SLE)", "warts": "Warts Treatment", "molluscum_contagiosum": "Molluscum Contagiosum", "viral_skin_infections_general": "General Viral Care", "seborrheic_keratosis": "Seborrheic Keratosis", "dermatofibroma": "Dermatofibroma", "lipoma": "Lipoma", "general_approach": "General Approach", "diabetes_related": "Diabetes-Related Skin", "thyroid_related": "Thyroid-Related Skin", "liver_disease": "Liver Disease Skin", "bullous_pemphigoid": "Bullous Pemphigoid", "wound_care": "Wound / Erosion Care", "topical_steroids": "Topical Steroids", "calcineurin_inhibitors": "Calcineurin Inhibitors", "supportive": "Supportive Care", } SKIP_KEYS = {"monitoring", "caution"} html = "" for key, value in meds.items(): if key in SKIP_KEYS: continue label = SECTION_LABELS.get(key, key.replace("_", " ").title()) if isinstance(value, list): items = "".join(f"
  • {item}
  • " for item in value) html += f"""
    💊 {label}
    """ elif isinstance(value, str): html += f"""
    📌 {label}

    {value}

    """ if meds.get("monitoring"): html += f"""
    📊 Monitoring: {meds['monitoring']}
    """ if meds.get("caution"): html += f"""
    ⚠️ Caution: {meds['caution']}
    """ return html def build_medicine_plain_block(meds): if not meds: return " No specific medication data available.\n" SKIP_KEYS = {"monitoring", "caution"} text = "" for key, value in meds.items(): if key in SKIP_KEYS: continue label = key.replace("_", " ").upper() if isinstance(value, list): text += f"\n [{label}]\n" + "".join(f" - {item}\n" for item in value) elif isinstance(value, str): text += f"\n [{label}]\n {value}\n" if meds.get("monitoring"): text += f"\n [MONITORING]\n {meds['monitoring']}\n" if meds.get("caution"): text += f"\n [CAUTION]\n {meds['caution']}\n" return text # ───────────────────────────────────────────── # Email Builders # ───────────────────────────────────────────── def build_email_html(name, age, email, phone, symptoms_text, disease, confidence, match_score, matching, missing, meds): report_date = datetime.now().strftime("%B %d, %Y %H:%M") matching_str = ", ".join(matching) if matching else "None" missing_str = ", ".join(missing) if missing else "None" medicine_block = build_medicine_html_block(meds) return f"""

    🩺 DermAI — Skin Condition Report

    Generated on {report_date}

    Patient: {name} Age: {age} Email: {email} {"Phone: " + phone + "" if phone else ""}

    Dear {name},

    Thank you for using DermAI Skin Condition Analyzer. Below is your personalized analysis report based on the image and symptoms provided.

    📋 Analysis Summary
    Predicted Condition{disease}
    Confidence Level{confidence}%
    Symptom Alignment{match_score}
    Symptoms You Reported{symptoms_text or "None"}
    Matching Symptoms{matching_str}
    Additional Notes{missing_str}
    💊 Recommended Medications & Treatment Protocol

    Based on standard clinical guidelines for the predicted condition. These are reference guidelines only — always follow your dermatologist's prescription.

    {medicine_block}
    🛡️ General Skin Care Tips
    ⚠️ Disclaimer: This report is for informational purposes only. Do NOT self-medicate. Always consult a qualified healthcare professional.
    """ def build_email_plain(name, age, symptoms_text, disease, confidence, match_score, matching, missing, meds): report_date = datetime.now().strftime("%B %d, %Y %H:%M") matching_str = ", ".join(matching) if matching else "None" missing_str = ", ".join(missing) if missing else "None" medicine_text = build_medicine_plain_block(meds) return f""" ============================================================ DermAI — Skin Condition Report Generated: {report_date} ============================================================ Dear {name}, PATIENT DETAILS Name : {name} Age : {age} ANALYSIS SUMMARY Predicted Condition : {disease} Confidence Level : {confidence}% Symptom Alignment : {match_score} Symptoms You Reported : {symptoms_text or "None"} Matching Symptoms : {matching_str} Additional Notes : {missing_str} RECOMMENDED MEDICATIONS & TREATMENT PROTOCOL {medicine_text} GENERAL SKIN CARE TIPS - Keep the affected area clean and dry. - Avoid scratching or rubbing the skin. - Use gentle, fragrance-free moisturizers if dryness is present. - Apply SPF 30+ sunscreen daily. - Consult a licensed dermatologist for a confirmed diagnosis. ------------------------------------------------------------ DISCLAIMER: This report is for informational purposes only and does NOT constitute a personal prescription. Do NOT self-medicate. Consult a qualified healthcare professional. ------------------------------------------------------------ DermAI — Skin Condition Analyzer | For educational use only """ # ───────────────────────────────────────────── # Send Email # ───────────────────────────────────────────── def send_report_email(recipient_email, recipient_name, age, phone, symptoms_text, disease, confidence, match_score, matching, missing, meds): try: msg = MIMEMultipart("alternative") msg["Subject"] = f"DermAI Report — {disease}" msg["From"] = f"DermAI <{SENDER_EMAIL}>" msg["To"] = recipient_email plain = build_email_plain(recipient_name, age, symptoms_text, disease, confidence, match_score, matching, missing, meds) html = build_email_html(recipient_name, age, recipient_email, phone, symptoms_text, disease, confidence, match_score, matching, missing, meds) msg.attach(MIMEText(plain, "plain")) msg.attach(MIMEText(html, "html")) with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server: server.login(SENDER_EMAIL, SENDER_PASSWORD) server.sendmail(SENDER_EMAIL, recipient_email, msg.as_string()) print(f"Email sent to {recipient_email}") return True except Exception as e: print(f"Email failed: {e}") return False # ───────────────────────────────────────────── # Routes # ───────────────────────────────────────────── @app.route('/') def serve_index(): return send_from_directory('.', 'index.html') @app.route('/predict', methods=['POST']) def predict(): try: if 'file' not in request.files: return jsonify({"error": "No file uploaded"}), 400 file = request.files['file'] if file.filename == '': return jsonify({"error": "No file selected"}), 400 user_info = {} if 'user_info' in request.form: try: user_info = json.loads(request.form['user_info']) except Exception: pass user_name = user_info.get("name", "Patient") user_age = user_info.get("age", "N/A") user_email = user_info.get("email", "") user_phone = user_info.get("phone", "") symptoms_text = user_info.get("symptoms", "") user_symptoms = [s.strip() for s in symptoms_text.replace(",", " ").split() if s.strip()] # Predict img_bytes = file.read() processed_img = preprocess_image(img_bytes) predictions = model.predict(processed_img)[0] top_idx = int(np.argmax(predictions)) confidence = float(predictions[top_idx] * 100) predicted_disease = class_names[top_idx] # ← loaded from class_names.json # Symptom matching known_symptoms = DISEASE_SYMPTOMS.get(predicted_disease, []) matching = [s for s in user_symptoms if any(s.lower() == k.lower() for k in known_symptoms)] missing = [k for k in known_symptoms if not any(k.lower() == u.lower() for u in user_symptoms)] match_score = ( f"{len(matching)} of {len(known_symptoms)} typical symptoms match" if known_symptoms else "No symptom data available" ) confidence_str = f"{confidence:.2f}" meds = get_medicine_info(predicted_disease) email_sent = False if user_email: email_sent = send_report_email( recipient_email = user_email, recipient_name = user_name, age = user_age, phone = user_phone, symptoms_text = symptoms_text, disease = predicted_disease, confidence = confidence_str, match_score = match_score, matching = matching, missing = missing, meds = meds ) return jsonify({ "disease": predicted_disease, "confidence": confidence_str, "match_score": match_score, "matching": matching, "missing": missing, "medicines": meds, "email_sent": email_sent }) except Exception as e: print("Error:", e) return jsonify({"error": "Prediction failed"}), 500 # ───────────────────────────────────────────── # Entry Point # ───────────────────────────────────────────── if __name__ == '__main__': print(f"\nDermAI server starting on port {PORT}\n") app.run(host='0.0.0.0', port=PORT, debug=False)