Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Stroke Risk Prediction</title> | |
| <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet"> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" /> | |
| <style> | |
| :root { | |
| --primary-color: #4e54c8; | |
| --secondary-color: #8f94fb; | |
| --success-color: #23ce6b; | |
| --danger-color: #ff6b6b; | |
| --light-bg: #f9f9ff; | |
| --dark-text: #333344; | |
| } | |
| body { | |
| background: linear-gradient(to right, var(--primary-color), var(--secondary-color)); | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| color: var(--dark-text); | |
| min-height: 100vh; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| padding: 20px 0; | |
| } | |
| .container { | |
| max-width: 900px; | |
| background-color: rgba(255, 255, 255, 0.95); | |
| padding: 40px; | |
| border-radius: 20px; | |
| box-shadow: 0 15px 30px rgba(0, 0, 0, 0.2); | |
| backdrop-filter: blur(5px); | |
| } | |
| h1 { | |
| background: linear-gradient(45deg, var(--primary-color), var(--secondary-color)); | |
| -webkit-background-clip: text; | |
| background-clip: text; | |
| color: transparent; | |
| font-weight: 700; | |
| margin-bottom: 30px; | |
| text-align: center; | |
| font-size: 2.5rem; | |
| } | |
| .form-group { | |
| margin-bottom: 25px; | |
| position: relative; | |
| } | |
| .form-control { | |
| border-radius: 10px; | |
| padding: 12px 15px; | |
| border: 2px solid #e0e0ff; | |
| transition: all 0.3s ease; | |
| } | |
| .form-control:focus { | |
| box-shadow: 0 0 0 3px rgba(78, 84, 200, 0.2); | |
| border-color: var(--primary-color); | |
| } | |
| label { | |
| font-weight: 600; | |
| margin-bottom: 8px; | |
| display: block; | |
| color: var(--primary-color); | |
| } | |
| .btn-predict { | |
| background: linear-gradient(45deg, var(--primary-color), var(--secondary-color)); | |
| border: none; | |
| width: 100%; | |
| padding: 15px; | |
| font-size: 18px; | |
| margin-top: 20px; | |
| border-radius: 10px; | |
| font-weight: 600; | |
| color: white; | |
| transition: all 0.3s ease; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .btn-predict:hover { | |
| transform: translateY(-3px); | |
| box-shadow: 0 7px 14px rgba(78, 84, 200, 0.3); | |
| } | |
| .btn-predict:after { | |
| content: ''; | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| width: 5px; | |
| height: 5px; | |
| background: rgba(255, 255, 255, 0.5); | |
| opacity: 0; | |
| border-radius: 100%; | |
| transform: scale(1, 1) translate(-50%, -50%); | |
| transform-origin: 50% 50%; | |
| } | |
| .btn-predict:focus:not(:active)::after { | |
| animation: ripple 1s ease-out; | |
| } | |
| @keyframes ripple { | |
| 0% { | |
| transform: scale(0, 0); | |
| opacity: 0.5; | |
| } | |
| 100% { | |
| transform: scale(20, 20); | |
| opacity: 0; | |
| } | |
| } | |
| .result-container { | |
| margin-top: 40px; | |
| padding: 30px; | |
| border-radius: 15px; | |
| display: none; | |
| transform: translateY(20px); | |
| transition: all 0.5s ease; | |
| } | |
| .high-risk { | |
| background-color: rgba(255, 107, 107, 0.2); | |
| border: 2px solid var(--danger-color); | |
| } | |
| .low-risk { | |
| background-color: rgba(35, 206, 107, 0.2); | |
| border: 2px solid var(--success-color); | |
| } | |
| .form-section { | |
| padding: 20px; | |
| border-radius: 15px; | |
| box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05); | |
| margin-bottom: 25px; | |
| background-color: white; | |
| } | |
| .section-title { | |
| border-bottom: 2px solid #e0e0ff; | |
| padding-bottom: 10px; | |
| margin-bottom: 20px; | |
| color: var(--primary-color); | |
| font-weight: 600; | |
| } | |
| .progress-bar { | |
| height: 8px; | |
| background-color: #e0e0ff; | |
| border-radius: 4px; | |
| margin-bottom: 30px; | |
| position: relative; | |
| } | |
| .progress-fill { | |
| height: 100%; | |
| border-radius: 4px; | |
| background: linear-gradient(45deg, var(--primary-color), var(--secondary-color)); | |
| width: 0; | |
| transition: width 0.3s ease; | |
| } | |
| .step-indicator { | |
| display: flex; | |
| justify-content: center; | |
| margin-bottom: 30px; | |
| } | |
| .step { | |
| width: 40px; | |
| height: 40px; | |
| border-radius: 50%; | |
| background-color: #e0e0ff; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-weight: 600; | |
| color: var(--dark-text); | |
| margin: 0 10px; | |
| position: relative; | |
| transition: all 0.3s ease; | |
| } | |
| .step.active { | |
| background: linear-gradient(45deg, var(--primary-color), var(--secondary-color)); | |
| color: white; | |
| transform: scale(1.1); | |
| } | |
| .step.completed { | |
| background: var(--success-color); | |
| color: white; | |
| } | |
| .step-line { | |
| position: absolute; | |
| height: 3px; | |
| background-color: #e0e0ff; | |
| top: 20px; | |
| left: 40px; | |
| width: calc(100% - 100px); | |
| z-index: -1; | |
| } | |
| .form-nav { | |
| display: flex; | |
| justify-content: space-between; | |
| margin-top: 20px; | |
| } | |
| .btn-nav { | |
| padding: 10px 20px; | |
| border-radius: 10px; | |
| background: white; | |
| border: 2px solid #e0e0ff; | |
| color: var(--primary-color); | |
| font-weight: 600; | |
| transition: all 0.3s ease; | |
| } | |
| .btn-nav:hover { | |
| background: var(--primary-color); | |
| color: white; | |
| border-color: var(--primary-color); | |
| } | |
| .form-step { | |
| display: none; | |
| } | |
| .form-step.active { | |
| display: block; | |
| animation: fadeIn 0.5s; | |
| } | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: translateY(20px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| .overlay { | |
| display: none; | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background-color: rgba(0, 0, 0, 0.5); | |
| z-index: 1000; | |
| } | |
| .loader { | |
| display: none; | |
| width: 48px; | |
| height: 48px; | |
| border: 5px solid var(--primary-color); | |
| border-bottom-color: transparent; | |
| border-radius: 50%; | |
| box-sizing: border-box; | |
| animation: rotation 1s linear infinite; | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| } | |
| @keyframes rotation { | |
| 0% { | |
| transform: translate(-50%, -50%) rotate(0deg); | |
| } | |
| 100% { | |
| transform: translate(-50%, -50%) rotate(360deg); | |
| } | |
| } | |
| .info-tooltip { | |
| display: inline-block; | |
| width: 18px; | |
| height: 18px; | |
| background-color: #e0e0ff; | |
| color: var(--primary-color); | |
| border-radius: 50%; | |
| text-align: center; | |
| line-height: 18px; | |
| font-size: 12px; | |
| margin-left: 5px; | |
| cursor: pointer; | |
| position: relative; | |
| } | |
| .info-tooltip .tooltip-text { | |
| visibility: hidden; | |
| width: 200px; | |
| background-color: #333; | |
| color: white; | |
| text-align: center; | |
| border-radius: 6px; | |
| padding: 5px; | |
| position: absolute; | |
| z-index: 1; | |
| bottom: 125%; | |
| left: 50%; | |
| margin-left: -100px; | |
| opacity: 0; | |
| transition: opacity 0.3s; | |
| font-size: 12px; | |
| font-weight: normal; | |
| } | |
| .info-tooltip:hover .tooltip-text { | |
| visibility: visible; | |
| opacity: 1; | |
| } | |
| .summary-label { | |
| font-weight: 600; | |
| color: var(--primary-color); | |
| } | |
| .summary-value { | |
| color: var(--dark-text); | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container animate__animated animate__fadeIn"> | |
| <div class="step-indicator"> | |
| <div class="step active" id="step-1">1</div> | |
| <div class="step" id="step-2">2</div> | |
| <div class="step" id="step-3">3</div> | |
| <div class="step-line"></div> | |
| </div> | |
| <h1>Stroke Risk Prediction</h1> | |
| <div class="progress-bar"> | |
| <div class="progress-fill" id="progress-bar"></div> | |
| </div> | |
| <form id="prediction-form"> | |
| <div class="form-step active" id="form-step-1"> | |
| <div class="form-section"> | |
| <h3 class="section-title">Personal Information</h3> | |
| <div class="row"> | |
| <div class="col-md-6"> | |
| <div class="form-group"> | |
| <label for="age">Age <span class="info-tooltip">i<span class="tooltip-text">Enter your age in years</span></span></label> | |
| <input type="number" class="form-control" id="age" name="age" required min="0" max="120" step="0.1"> | |
| </div> | |
| </div> | |
| <div class="col-md-6"> | |
| <div class="form-group"> | |
| <label for="gender">Gender <span class="info-tooltip">i<span class="tooltip-text">Select your gender</span></span></label> | |
| <select class="form-control" id="gender" name="gender" required> | |
| <option value="">Select Gender</option> | |
| <option value="Male">Male</option> | |
| <option value="Female">Female</option> | |
| <option value="Other">Other</option> | |
| </select> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="row"> | |
| <div class="col-md-6"> | |
| <div class="form-group"> | |
| <label for="ever_married">Ever Married <span class="info-tooltip">i<span class="tooltip-text">Have you ever been married?</span></span></label> | |
| <select class="form-control" id="ever_married" name="ever_married" required> | |
| <option value="">Select</option> | |
| <option value="Yes">Yes</option> | |
| <option value="No">No</option> | |
| </select> | |
| </div> | |
| </div> | |
| <div class="col-md-6"> | |
| <div class="form-group"> | |
| <label for="residence_type">Residence Type <span class="info-tooltip">i<span class="tooltip-text">Urban or rural area</span></span></label> | |
| <select class="form-control" id="residence_type" name="residence_type" required> | |
| <option value="">Select</option> | |
| <option value="Urban">Urban</option> | |
| <option value="Rural">Rural</option> | |
| </select> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="row"> | |
| <div class="col-md-12"> | |
| <div class="form-group"> | |
| <label for="work_type">Work Type <span class="info-tooltip">i<span class="tooltip-text">Type of employment</span></span></label> | |
| <select class="form-control" id="work_type" name="work_type" required> | |
| <option value="">Select Work Type</option> | |
| <option value="Private">Private</option> | |
| <option value="Self-employed">Self-employed</option> | |
| <option value="Govt_job">Government Job</option> | |
| <option value="Never_worked">Never worked</option> | |
| </select> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="form-nav"> | |
| <div></div> | |
| <button type="button" class="btn btn-nav" id="next-1">Next <i class="fas fa-arrow-right"></i></button> | |
| </div> | |
| </div> | |
| <div class="form-step" id="form-step-2"> | |
| <div class="form-section"> | |
| <h3 class="section-title">Health Information</h3> | |
| <div class="row"> | |
| <div class="col-md-6"> | |
| <div class="form-group"> | |
| <label for="hypertension">Hypertension <span class="info-tooltip">i<span class="tooltip-text">Do you have hypertension?</span></span></label> | |
| <select class="form-control" id="hypertension" name="hypertension" required> | |
| <option value="">Select</option> | |
| <option value="1">Yes</option> | |
| <option value="0">No</option> | |
| </select> | |
| </div> | |
| </div> | |
| <div class="col-md-6"> | |
| <div class="form-group"> | |
| <label for="heart_disease">Heart Disease <span class="info-tooltip">i<span class="tooltip-text">Do you have heart disease?</span></span></label> | |
| <select class="form-control" id="heart_disease" name="heart_disease" required> | |
| <option value="">Select</option> | |
| <option value="1">Yes</option> | |
| <option value="0">No</option> | |
| </select> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="row"> | |
| <div class="col-md-6"> | |
| <div class="form-group"> | |
| <label for="avg_glucose_level">Avg. Glucose Level <span class="info-tooltip">i<span class="tooltip-text">Normal fasting blood sugar is under 100 mg/dL</span></span></label> | |
| <input type="number" class="form-control" id="avg_glucose_level" name="avg_glucose_level" required min="0" step="0.01"> | |
| </div> | |
| </div> | |
| <div class="col-md-6"> | |
| <div class="form-group"> | |
| <label for="bmi">BMI <span class="info-tooltip">i<span class="tooltip-text">Body Mass Index (weight in kg / height in m²)</span></span></label> | |
| <input type="number" class="form-control" id="bmi" name="bmi" required min="0" max="100" step="0.01"> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="row"> | |
| <div class="col-md-12"> | |
| <div class="form-group"> | |
| <label for="smoking_status">Smoking Status <span class="info-tooltip">i<span class="tooltip-text">Your smoking habits</span></span></label> | |
| <select class="form-control" id="smoking_status" name="smoking_status" required> | |
| <option value="">Select Smoking Status</option> | |
| <option value="formerly smoked">Formerly Smoked</option> | |
| <option value="never smoked">Never Smoked</option> | |
| <option value="smokes">Currently Smokes</option> | |
| <option value="Unknown">Unknown</option> | |
| </select> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="form-nav"> | |
| <button type="button" class="btn btn-nav" id="prev-2">Previous</button> | |
| <button type="button" class="btn btn-nav" id="next-2">Next</button> | |
| </div> | |
| </div> | |
| <div class="form-step" id="form-step-3"> | |
| <div class="form-section"> | |
| <h3 class="section-title">Summary</h3> | |
| <p>Please review your information below before submitting:</p> | |
| <div class="row"> | |
| <div class="col-md-6"> | |
| <p><span class="summary-label">Age:</span> <span class="summary-value" id="summary-age"></span></p> | |
| <p><span class="summary-label">Gender:</span> <span class="summary-value" id="summary-gender"></span></p> | |
| <p><span class="summary-label">Ever Married:</span> <span class="summary-value" id="summary-married"></span></p> | |
| <p><span class="summary-label">Residence Type:</span> <span class="summary-value" id="summary-residence"></span></p> | |
| <p><span class="summary-label">Work Type:</span> <span class="summary-value" id="summary-work"></span></p> | |
| </div> | |
| <div class="col-md-6"> | |
| <p><span class="summary-label">Hypertension:</span> <span class="summary-value" id="summary-hypertension"></span></p> | |
| <p><span class="summary-label">Heart Disease:</span> <span class="summary-value" id="summary-heart"></span></p> | |
| <p><span class="summary-label">Avg. Glucose Level:</span> <span class="summary-value" id="summary-glucose"></span> mg/dL</p> | |
| <p><span class="summary-label">BMI:</span> <span class="summary-value" id="summary-bmi"></span></p> | |
| <p><span class="summary-label">Smoking Status:</span> <span class="summary-value" id="summary-smoking"></span></p> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="form-nav"> | |
| <button type="button" class="btn btn-nav" id="prev-3">Previous</button> | |
| <button type="submit" class="btn btn-predict">Predict Stroke Risk</button> | |
| </div> | |
| </div> | |
| </form> | |
| <div id="result" class="result-container animate__animated"> | |
| <h3 id="result-title" class="text-center mb-4">Prediction Result</h3> | |
| <p id="prediction-result" class="text-center fs-4 fw-bold"></p> | |
| <div class="text-center mt-4"> | |
| <button type="button" class="btn btn-nav" id="start-over">Start Over</button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="overlay" id="loading-overlay"> | |
| <div class="loader"></div> | |
| </div> | |
| <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/js/all.min.js"></script> | |
| <script> | |
| $(document).ready(function() { | |
| let currentStep = 1; | |
| const totalSteps = 3; | |
| // Update progress bar | |
| function updateProgressBar() { | |
| const progressPercent = ((currentStep - 1) / (totalSteps - 1)) * 100; | |
| $('#progress-bar').css('width', progressPercent + '%'); | |
| } | |
| // Navigate to next step | |
| function nextStep() { | |
| if (validateStep(currentStep)) { | |
| $(`#form-step-${currentStep}`).removeClass('active'); | |
| currentStep++; | |
| $(`#form-step-${currentStep}`).addClass('active animate__animated animate__fadeIn'); | |
| // Update step indicators | |
| $(`.step`).removeClass('active'); | |
| $(`#step-${currentStep}`).addClass('active'); | |
| // Mark previous steps as completed | |
| for (let i = 1; i < currentStep; i++) { | |
| $(`#step-${i}`).addClass('completed'); | |
| } | |
| updateProgressBar(); | |
| // If final step, update summary | |
| if (currentStep === 3) { | |
| updateSummary(); | |
| } | |
| } | |
| } | |
| // Navigate to previous step | |
| function prevStep() { | |
| $(`#form-step-${currentStep}`).removeClass('active'); | |
| currentStep--; | |
| $(`#form-step-${currentStep}`).addClass('active animate__animated animate__fadeIn'); | |
| // Update step indicators | |
| $(`.step`).removeClass('active'); | |
| $(`#step-${currentStep}`).addClass('active'); | |
| updateProgressBar(); | |
| } | |
| // Validate current step | |
| function validateStep(step) { | |
| let isValid = true; | |
| $(`#form-step-${step} input, #form-step-${step} select`).each(function() { | |
| if ($(this).prop('required') && !$(this).val()) { | |
| isValid = false; | |
| $(this).addClass('is-invalid'); | |
| } else { | |
| $(this).removeClass('is-invalid'); | |
| } | |
| }); | |
| return isValid; | |
| } | |
| // Update summary before submission | |
| function updateSummary() { | |
| $('#summary-age').text($('#age').val() || 'Not specified'); | |
| $('#summary-gender').text($('#gender').val() || 'Not specified'); | |
| $('#summary-married').text($('#ever_married').val() || 'Not specified'); | |
| $('#summary-residence').text($('#residence_type').val() || 'Not specified'); | |
| $('#summary-work').text($('#work_type').val() || 'Not specified'); | |
| $('#summary-hypertension').text($('#hypertension').val() === '1' ? 'Yes' : $('#hypertension').val() === '0' ? 'No' : 'Not specified'); | |
| $('#summary-heart').text($('#heart_disease').val() === '1' ? 'Yes' : $('#heart_disease').val() === '0' ? 'No' : 'Not specified'); | |
| $('#summary-glucose').text($('#avg_glucose_level').val() || 'Not specified'); | |
| $('#summary-bmi').text($('#bmi').val() || 'Not specified'); | |
| $('#summary-smoking').text($('#smoking_status').val() || 'Not specified'); | |
| } | |
| // Navigation button click handlers | |
| $('#next-1').click(nextStep); | |
| $('#next-2').click(nextStep); | |
| $('#prev-2').click(prevStep); | |
| $('#prev-3').click(prevStep); | |
| // Start over button | |
| $('#start-over').click(function() { | |
| $('#prediction-form')[0].reset(); | |
| $('#result').removeClass('animate__fadeIn').addClass('animate__fadeOut'); | |
| setTimeout(function() { | |
| $('#result').hide().removeClass('animate__fadeOut'); | |
| // Reset to first step | |
| $('.form-step').removeClass('active'); | |
| $('#form-step-1').addClass('active'); | |
| currentStep = 1; | |
| // Reset step indicators | |
| $('.step').removeClass('active completed'); | |
| $('#step-1').addClass('active'); | |
| updateProgressBar(); | |
| }, 500); | |
| }); | |
| // Field validation on input change | |
| $('input, select').on('change', function() { | |
| if ($(this).prop('required') && !$(this).val()) { | |
| $(this).addClass('is-invalid'); | |
| } else { | |
| $(this).removeClass('is-invalid'); | |
| } | |
| }); | |
| // Form submission with animation | |
| $('#prediction-form').on('submit', function(e) { | |
| e.preventDefault(); | |
| if (validateStep(currentStep)) { | |
| console.log("Form validated, initiating prediction request"); | |
| $('#loading-overlay').fadeIn(); | |
| // Collect form data | |
| const formData = { | |
| age: $('#age').val(), | |
| gender: $('#gender').val(), | |
| ever_married: $('#ever_married').val(), | |
| residence_type: $('#residence_type').val(), | |
| work_type: $('#work_type').val(), | |
| hypertension: $('#hypertension').val(), | |
| heart_disease: $('#heart_disease').val(), | |
| avg_glucose_level: $('#avg_glucose_level').val(), | |
| bmi: $('#bmi').val(), | |
| smoking_status: $('#smoking_status').val() | |
| }; | |
| console.log("Form data collected:", formData); | |
| // Send data to Flask backend | |
| $.ajax({ | |
| url: '/predict', | |
| type: 'POST', | |
| contentType: 'application/json', | |
| data: JSON.stringify({ input: formData }), | |
| beforeSend: function() { | |
| console.log("Sending AJAX request to /predict"); | |
| }, | |
| success: function(response) { | |
| console.log("Received response:", response); | |
| $('#loading-overlay').fadeOut(); | |
| if (response.success) { | |
| console.log("Prediction successful, rendering result"); | |
| $('#prediction-result').text(response.prediction); | |
| $('#result').removeClass('high-risk low-risk'); | |
| if (response.prediction === 'Stroke Risk') { | |
| $('#result').addClass('high-risk'); | |
| } else { | |
| $('#result').addClass('low-risk'); | |
| } | |
| $('#result').show().removeClass('animate__fadeOut').addClass('animate__fadeIn'); | |
| $('#prediction-form').hide(); | |
| } else { | |
| console.error("Prediction failed:", response.error); | |
| alert('Error: ' + response.error); | |
| $('#result').hide(); | |
| } | |
| }, | |
| error: function(xhr, status, error) { | |
| console.error("AJAX request failed:", { status: status, error: error, response: xhr.responseText }); | |
| $('#loading-overlay').fadeOut(); | |
| $('#prediction-result').text("Error occurred while processing your request."); | |
| $('#result').removeClass('high-risk low-risk').addClass('high-risk'); | |
| $('#result').show().removeClass('animate__fadeOut').addClass('animate__fadeIn'); | |
| $('#prediction-form').hide(); | |
| }, | |
| complete: function() { | |
| console.log("AJAX request completed"); | |
| } | |
| }); | |
| } else { | |
| console.log("Form validation failed"); | |
| } | |
| }); | |
| }); | |
| </script> | |
| </body> | |
| </html> |