Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Exoplanet Prediction System</title> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| background: linear-gradient(135deg, #0f0c29 0%, #302b63 50%, #24243e 100%); | |
| min-height: 100vh; | |
| color: #fff; | |
| overflow-x: hidden; | |
| } | |
| .stars { | |
| position: fixed; | |
| width: 100%; | |
| height: 100%; | |
| pointer-events: none; | |
| } | |
| .star { | |
| position: absolute; | |
| width: 2px; | |
| height: 2px; | |
| background: white; | |
| border-radius: 50%; | |
| animation: twinkle 3s infinite; | |
| } | |
| @keyframes twinkle { | |
| 0%, 100% { opacity: 0.3; } | |
| 50% { opacity: 1; } | |
| } | |
| .container { | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| padding: 40px 20px; | |
| position: relative; | |
| z-index: 1; | |
| } | |
| .header { | |
| text-align: center; | |
| margin-bottom: 50px; | |
| } | |
| .header h1 { | |
| font-size: 3rem; | |
| background: linear-gradient(45deg, #667eea 0%, #764ba2 100%); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| margin-bottom: 10px; | |
| text-shadow: 0 0 30px rgba(102, 126, 234, 0.5); | |
| } | |
| .header p { | |
| color: #a0aec0; | |
| font-size: 1.1rem; | |
| } | |
| .tabs { | |
| display: flex; | |
| gap: 10px; | |
| margin-bottom: 30px; | |
| justify-content: center; | |
| } | |
| .tab { | |
| padding: 15px 30px; | |
| background: rgba(255, 255, 255, 0.05); | |
| border: 2px solid rgba(255, 255, 255, 0.1); | |
| border-radius: 15px; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| font-size: 1rem; | |
| color: #fff; | |
| backdrop-filter: blur(10px); | |
| } | |
| .tab:hover { | |
| background: rgba(255, 255, 255, 0.1); | |
| transform: translateY(-2px); | |
| } | |
| .tab.active { | |
| background: linear-gradient(45deg, #667eea 0%, #764ba2 100%); | |
| border-color: #667eea; | |
| box-shadow: 0 10px 30px rgba(102, 126, 234, 0.3); | |
| } | |
| .tab-content { | |
| display: none; | |
| animation: fadeIn 0.5s ease; | |
| } | |
| .tab-content.active { | |
| display: block; | |
| } | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: translateY(20px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| .card { | |
| background: rgba(255, 255, 255, 0.05); | |
| backdrop-filter: blur(10px); | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| border-radius: 20px; | |
| padding: 40px; | |
| box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); | |
| } | |
| .form-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); | |
| gap: 25px; | |
| margin-bottom: 30px; | |
| } | |
| .form-group { | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| label { | |
| margin-bottom: 8px; | |
| color: #a0aec0; | |
| font-size: 0.9rem; | |
| font-weight: 500; | |
| } | |
| input[type="number"], input[type="file"] { | |
| padding: 12px 15px; | |
| background: rgba(255, 255, 255, 0.05); | |
| border: 1px solid rgba(255, 255, 255, 0.2); | |
| border-radius: 10px; | |
| color: #fff; | |
| font-size: 1rem; | |
| transition: all 0.3s ease; | |
| } | |
| input[type="number"]:focus, input[type="file"]:focus, select:focus { | |
| outline: none; | |
| border-color: #667eea; | |
| box-shadow: 0 0 20px rgba(102, 126, 234, 0.3); | |
| } | |
| select { | |
| background: rgba(255, 255, 255, 0.05); | |
| border: 1px solid rgba(255, 255, 255, 0.2); | |
| border-radius: 10px; | |
| color: #fff; | |
| padding: 12px 15px; | |
| font-size: 1rem; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| } | |
| select option { | |
| background: #302b63; | |
| color: #fff; | |
| } | |
| select:hover { | |
| border-color: rgba(255, 255, 255, 0.4); | |
| } | |
| input::placeholder { | |
| color: rgba(255, 255, 255, 0.3); | |
| } | |
| .btn { | |
| padding: 15px 40px; | |
| background: linear-gradient(45deg, #667eea 0%, #764ba2 100%); | |
| border: none; | |
| border-radius: 12px; | |
| color: #fff; | |
| font-size: 1.1rem; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| box-shadow: 0 10px 30px rgba(102, 126, 234, 0.3); | |
| width: 100%; | |
| } | |
| .btn:hover { | |
| transform: translateY(-3px); | |
| box-shadow: 0 15px 40px rgba(102, 126, 234, 0.5); | |
| } | |
| .btn:disabled { | |
| opacity: 0.6; | |
| cursor: not-allowed; | |
| transform: none; | |
| } | |
| .result { | |
| margin-top: 30px; | |
| padding: 25px; | |
| background: rgba(255, 255, 255, 0.05); | |
| border-radius: 15px; | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| animation: slideUp 0.5s ease; | |
| } | |
| @keyframes slideUp { | |
| from { opacity: 0; transform: translateY(30px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| .result h3 { | |
| margin-bottom: 15px; | |
| color: #667eea; | |
| font-size: 1.5rem; | |
| } | |
| .result-item { | |
| display: flex; | |
| justify-content: space-between; | |
| padding: 10px 0; | |
| border-bottom: 1px solid rgba(255, 255, 255, 0.1); | |
| } | |
| .result-item:last-child { | |
| border-bottom: none; | |
| } | |
| .result-label { | |
| color: #a0aec0; | |
| } | |
| .result-value { | |
| font-weight: 600; | |
| color: #fff; | |
| } | |
| .badge { | |
| display: inline-block; | |
| padding: 8px 20px; | |
| border-radius: 20px; | |
| font-size: 0.9rem; | |
| font-weight: 600; | |
| } | |
| .badge.positive { | |
| background: linear-gradient(45deg, #11998e 0%, #38ef7d 100%); | |
| } | |
| .badge.negative { | |
| background: linear-gradient(45deg, #ee0979 0%, #ff6a00 100%); | |
| } | |
| .upload-area { | |
| border: 2px dashed rgba(255, 255, 255, 0.3); | |
| border-radius: 15px; | |
| padding: 40px; | |
| text-align: center; | |
| margin-bottom: 30px; | |
| transition: all 0.3s ease; | |
| cursor: pointer; | |
| } | |
| .upload-area:hover { | |
| border-color: #667eea; | |
| background: rgba(102, 126, 234, 0.1); | |
| } | |
| .upload-icon { | |
| font-size: 3rem; | |
| margin-bottom: 15px; | |
| opacity: 0.5; | |
| } | |
| .file-info { | |
| margin-top: 15px; | |
| color: #667eea; | |
| font-weight: 500; | |
| } | |
| .loading { | |
| display: inline-block; | |
| width: 20px; | |
| height: 20px; | |
| border: 3px solid rgba(255, 255, 255, 0.3); | |
| border-radius: 50%; | |
| border-top-color: #fff; | |
| animation: spin 1s linear infinite; | |
| margin-right: 10px; | |
| vertical-align: middle; | |
| } | |
| @keyframes spin { | |
| to { transform: rotate(360deg); } | |
| } | |
| .error { | |
| background: rgba(238, 9, 121, 0.1); | |
| border: 1px solid rgba(238, 9, 121, 0.3); | |
| color: #ff6b9d; | |
| padding: 15px; | |
| border-radius: 10px; | |
| margin-top: 15px; | |
| } | |
| .info-tooltip { | |
| display: inline-block; | |
| margin-left: 5px; | |
| width: 16px; | |
| height: 16px; | |
| background: rgba(255, 255, 255, 0.2); | |
| border-radius: 50%; | |
| text-align: center; | |
| line-height: 16px; | |
| font-size: 0.7rem; | |
| cursor: help; | |
| } | |
| .fireworks-container { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| pointer-events: none; | |
| z-index: 9999; | |
| } | |
| .firework { | |
| position: absolute; | |
| width: 4px; | |
| height: 4px; | |
| border-radius: 50%; | |
| animation: firework-launch 1s ease-out forwards; | |
| } | |
| @keyframes firework-launch { | |
| 0% { | |
| transform: translateY(0); | |
| opacity: 1; | |
| } | |
| 100% { | |
| transform: translateY(-400px); | |
| opacity: 0; | |
| } | |
| } | |
| .particle { | |
| position: absolute; | |
| width: 6px; | |
| height: 6px; | |
| border-radius: 50%; | |
| animation: particle-explode 1s ease-out forwards; | |
| } | |
| @keyframes particle-explode { | |
| 0% { | |
| transform: translate(0, 0) scale(1); | |
| opacity: 1; | |
| } | |
| 100% { | |
| opacity: 0; | |
| transform: scale(0); | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="stars" id="stars"></div> | |
| <div class="fireworks-container" id="fireworks"></div> | |
| <div class="container"> | |
| <div class="header"> | |
| <h1>🪐 Exoplanet Detection System</h1> | |
| <p>AI-Powered Classification of Kepler Objects of Interest</p> | |
| </div> | |
| <div class="tabs"> | |
| <div class="tab active" onclick="switchTab('manual')">Manual Prediction</div> | |
| <div class="tab" onclick="switchTab('batch')">Batch Upload</div> | |
| </div> | |
| <div id="manual-tab" class="tab-content active"> | |
| <div class="card"> | |
| <form id="prediction-form"> | |
| <div class="form-group" style="margin-bottom: 30px;"> | |
| <label>Select Model <span class="info-tooltip" title="Choose which ML model to use for prediction">?</span></label> | |
| <select id="model-select" name="model" style="padding: 12px 15px; background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 10px; color: #fff; font-size: 1rem; width: 100%; cursor: pointer; transition: all 0.3s ease;"> | |
| <option value="random_forest">Random Forest</option> | |
| <option value="xgboost">XGBoost</option> | |
| <option value="ensemble">XGB + RF</option> | |
| <!-- <option value="logistic_regression">Logistic Regression</option> | |
| <option value="svm">Support Vector Machine</option> | |
| <option value="neural_network">Neural Network</option> | |
| <option value="gradient_boosting">Gradient Boosting</option> --> | |
| </select> | |
| </div> | |
| <div class="form-grid"> | |
| <div class="form-group"> | |
| <label>Signal-to-Noise Ratio <span class="info-tooltip" title="Transit signal-to-noise ratio">?</span></label> | |
| <input type="number" step="0.01" name="koi_model_snr" placeholder="e.g., 15.5" required> | |
| </div> | |
| <div class="form-group"> | |
| <label>Planetary Radius (Earth radii) <span class="info-tooltip" title="Planet size relative to Earth">?</span></label> | |
| <input type="number" step="0.01" name="koi_prad" placeholder="e.g., 2.3" required> | |
| </div> | |
| <div class="form-group"> | |
| <label>Stellar Eclipse Flag <span class="info-tooltip" title="0 or 1">?</span></label> | |
| <input type="number" min="0" max="1" name="koi_fpflag_ss" placeholder="0 or 1" required> | |
| </div> | |
| <div class="form-group"> | |
| <label>Centroid Offset Flag <span class="info-tooltip" title="0 or 1">?</span></label> | |
| <input type="number" min="0" max="1" name="koi_fpflag_co" placeholder="0 or 1" required> | |
| </div> | |
| <div class="form-group"> | |
| <label>Orbital Period (days) <span class="info-tooltip" title="Time for one complete orbit">?</span></label> | |
| <input type="number" step="0.01" name="koi_period" placeholder="e.g., 10.5" required> | |
| </div> | |
| <div class="form-group"> | |
| <label>Transit Depth (ppm) <span class="info-tooltip" title="Parts per million">?</span></label> | |
| <input type="number" step="0.01" name="koi_depth" placeholder="e.g., 500.0" required> | |
| </div> | |
| <div class="form-group"> | |
| <label>Not Transit-Like Flag <span class="info-tooltip" title="0 or 1">?</span></label> | |
| <input type="number" min="0" max="1" name="koi_fpflag_nt" placeholder="0 or 1" required> | |
| </div> | |
| <div class="form-group"> | |
| <label>Insolation Flux (Earth units) <span class="info-tooltip" title="Amount of stellar radiation">?</span></label> | |
| <input type="number" step="0.01" name="koi_insol" placeholder="e.g., 1.2" required> | |
| </div> | |
| </div> | |
| <button type="submit" class="btn" id="predict-btn"> | |
| Predict Exoplanet | |
| </button> | |
| </form> | |
| <div id="result-container"></div> | |
| </div> | |
| </div> | |
| <div id="batch-tab" class="tab-content"> | |
| <div class="card"> | |
| <div class="form-group" style="margin-bottom: 30px;"> | |
| <label>Select Model <span class="info-tooltip" title="Choose which ML model to use for batch predictions">?</span></label> | |
| <select id="batch-model-select" name="model" style="padding: 12px 15px; background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 10px; color: #fff; font-size: 1rem; width: 100%; cursor: pointer; transition: all 0.3s ease;"> | |
| <option value="random_forest">Random Forest</option> | |
| <option value="xgboost">XGBoost</option> | |
| <option value="ensemble">XGB + RF</option> | |
| <!-- <option value="logistic_regression">Logistic Regression</option> | |
| <option value="svm">Support Vector Machine</option> | |
| <option value="neural_network">Neural Network</option> | |
| <option value="gradient_boosting">Gradient Boosting</option> --> | |
| </select> | |
| </div> | |
| <div class="upload-area" onclick="document.getElementById('csv-file').click()"> | |
| <div class="upload-icon">📁</div> | |
| <h3>Upload CSV File</h3> | |
| <p style="color: #a0aec0; margin-top: 10px;">Click to browse or drag and drop your CSV file</p> | |
| <input type="file" id="csv-file" accept=".csv" style="display: none;" onchange="handleFileSelect(event)"> | |
| <div id="file-info" class="file-info"></div> | |
| </div> | |
| <button class="btn" id="batch-btn" onclick="uploadBatch()" disabled> | |
| Process Batch Predictions | |
| </button> | |
| <div id="batch-result-container"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Create stars | |
| const starsContainer = document.getElementById('stars'); | |
| for (let i = 0; i < 100; i++) { | |
| const star = document.createElement('div'); | |
| star.className = 'star'; | |
| star.style.left = Math.random() * 100 + '%'; | |
| star.style.top = Math.random() * 100 + '%'; | |
| star.style.animationDelay = Math.random() * 3 + 's'; | |
| starsContainer.appendChild(star); | |
| } | |
| // API endpoint - update this to your actual API URL | |
| const API_URL = 'https://pacman2223-exo-ml.hf.space'; | |
| function switchTab(tab) { | |
| document.querySelectorAll('.tab').forEach(t => t.classList.remove('active')); | |
| document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active')); | |
| event.target.classList.add('active'); | |
| document.getElementById(tab + '-tab').classList.add('active'); | |
| } | |
| document.getElementById('prediction-form').addEventListener('submit', async (e) => { | |
| e.preventDefault(); | |
| const btn = document.getElementById('predict-btn'); | |
| btn.disabled = true; | |
| btn.innerHTML = '<span class="loading"></span>Predicting...'; | |
| const formData = new FormData(e.target); | |
| const data = Object.fromEntries(formData); | |
| // Extract model selection | |
| const selectedModel = data.model; | |
| delete data.model; | |
| // Convert to numbers | |
| Object.keys(data).forEach(key => { | |
| data[key] = parseFloat(data[key]); | |
| }); | |
| try { | |
| const response = await fetch(`${API_URL}/predict?model=${selectedModel}`, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify(data) | |
| }); | |
| const result = await response.json(); | |
| displayResult(result, selectedModel); | |
| } catch (error) { | |
| document.getElementById('result-container').innerHTML = ` | |
| <div class="error"> | |
| <strong>Error:</strong> ${error.message}<br> | |
| <small>Make sure the API server is running at ${API_URL}</small> | |
| </div> | |
| `; | |
| } finally { | |
| btn.disabled = false; | |
| btn.innerHTML = 'Predict Exoplanet'; | |
| } | |
| }); | |
| function displayResult(result, modelName) { | |
| let badgeClass, statusText; | |
| if (result.prediction === 1) { | |
| badgeClass = 'positive'; | |
| statusText = 'Confirmed Exoplanet'; | |
| } else if (result.prediction === 2) { | |
| badgeClass = 'positive'; | |
| statusText = 'Exoplanet Candidate'; | |
| } else { | |
| badgeClass = 'negative'; | |
| statusText = 'False Positive'; | |
| } | |
| const modelDisplay = modelName ? modelName.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()) : 'N/A'; | |
| const html = ` | |
| <div class="result"> | |
| <h3>Prediction Results</h3> | |
| <div class="result-item"> | |
| <span class="result-label">Model Used:</span> | |
| <span class="result-value">${modelDisplay}</span> | |
| </div> | |
| <div class="result-item"> | |
| <span class="result-label">Classification:</span> | |
| <span class="badge ${badgeClass}">${result.classification}</span> | |
| </div> | |
| <div class="result-item"> | |
| <span class="result-label">Confidence:</span> | |
| <span class="result-value">${(result.probability * 100).toFixed(2)}%</span> | |
| </div> | |
| <div class="result-item"> | |
| <span class="result-label">Status:</span> | |
| <span class="result-value">${statusText}</span> | |
| </div> | |
| </div> | |
| `; | |
| document.getElementById('result-container').innerHTML = html; | |
| // Launch fireworks if exoplanet is confirmed! | |
| if (result.prediction === 1) { | |
| launchFireworks(); | |
| } | |
| } | |
| function launchFireworks() { | |
| const container = document.getElementById('fireworks'); | |
| const colors = ['#ff0844', '#ffb199', '#ffd23f', '#00d9ff', '#7b5cff', '#ff006e', '#8338ec', '#3a86ff']; | |
| // Launch 15 fireworks over 3 seconds | |
| for (let i = 0; i < 15; i++) { | |
| setTimeout(() => { | |
| createFirework(container, colors); | |
| }, i * 200); | |
| } | |
| } | |
| function createFirework(container, colors) { | |
| const startX = Math.random() * window.innerWidth; | |
| const startY = window.innerHeight; | |
| const endX = startX; | |
| const endY = Math.random() * (window.innerHeight * 0.5) + 100; | |
| const color = colors[Math.floor(Math.random() * colors.length)]; | |
| // Create launch trail | |
| const firework = document.createElement('div'); | |
| firework.className = 'firework'; | |
| firework.style.left = startX + 'px'; | |
| firework.style.top = startY + 'px'; | |
| firework.style.backgroundColor = color; | |
| container.appendChild(firework); | |
| // Explode after launch | |
| setTimeout(() => { | |
| explode(container, endX, endY, color); | |
| firework.remove(); | |
| }, 1000); | |
| } | |
| function explode(container, x, y, color) { | |
| const particleCount = 50; | |
| for (let i = 0; i < particleCount; i++) { | |
| const particle = document.createElement('div'); | |
| particle.className = 'particle'; | |
| particle.style.left = x + 'px'; | |
| particle.style.top = y + 'px'; | |
| particle.style.backgroundColor = color; | |
| const angle = (Math.PI * 2 * i) / particleCount; | |
| const velocity = 100 + Math.random() * 100; | |
| const tx = Math.cos(angle) * velocity; | |
| const ty = Math.sin(angle) * velocity; | |
| particle.style.setProperty('--tx', tx + 'px'); | |
| particle.style.setProperty('--ty', ty + 'px'); | |
| particle.style.animation = `particle-explode ${0.8 + Math.random() * 0.4}s ease-out forwards`; | |
| particle.style.transform = `translate(${tx}px, ${ty}px)`; | |
| container.appendChild(particle); | |
| setTimeout(() => particle.remove(), 1500); | |
| } | |
| } | |
| let selectedFile = null; | |
| function handleFileSelect(event) { | |
| selectedFile = event.target.files[0]; | |
| if (selectedFile) { | |
| document.getElementById('file-info').innerHTML = `Selected: ${selectedFile.name} (${(selectedFile.size / 1024).toFixed(2)} KB)`; | |
| document.getElementById('batch-btn').disabled = false; | |
| } | |
| } | |
| async function uploadBatch() { | |
| if (!selectedFile) return; | |
| const btn = document.getElementById('batch-btn'); | |
| btn.disabled = true; | |
| btn.innerHTML = '<span class="loading"></span>Processing...'; | |
| const selectedModel = document.getElementById('batch-model-select').value; | |
| const formData = new FormData(); | |
| formData.append('file', selectedFile); | |
| try { | |
| const response = await fetch(`${API_URL}/predict/batch?model=${selectedModel}`, { | |
| method: 'POST', | |
| body: formData | |
| }); | |
| if (response.ok) { | |
| const blob = await response.blob(); | |
| const url = window.URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = `predictions_${selectedModel}_${selectedFile.name}`; | |
| document.body.appendChild(a); | |
| a.click(); | |
| window.URL.revokeObjectURL(url); | |
| document.body.removeChild(a); | |
| const modelDisplay = selectedModel.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()); | |
| document.getElementById('batch-result-container').innerHTML = ` | |
| <div class="result"> | |
| <h3>✅ Success!</h3> | |
| <p>Your predictions using <strong>${modelDisplay}</strong> have been downloaded.</p> | |
| </div> | |
| `; | |
| } else { | |
| throw new Error('Upload failed'); | |
| } | |
| } catch (error) { | |
| document.getElementById('batch-result-container').innerHTML = ` | |
| <div class="error"> | |
| <strong>Error:</strong> ${error.message}<br> | |
| <small>Make sure the API server is running and the CSV has all required columns.</small> | |
| </div> | |
| `; | |
| } finally { | |
| btn.disabled = false; | |
| btn.innerHTML = 'Process Batch Predictions'; | |
| } | |
| } | |
| </script> | |
| </body> | |
| </html> | |