Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Food Delivery Time Prediction</title> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: 'Inter', sans-serif; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| min-height: 100vh; | |
| padding: 20px; | |
| } | |
| .container { | |
| max-width: 1000px; | |
| margin: 0 auto; | |
| background: rgba(255, 255, 255, 0.95); | |
| backdrop-filter: blur(20px); | |
| border-radius: 24px; | |
| box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); | |
| overflow: hidden; | |
| } | |
| .header { | |
| background: linear-gradient(135deg, #ff6b6b, #ee5a24); | |
| padding: 40px; | |
| text-align: center; | |
| color: white; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .header::before { | |
| content: ''; | |
| position: absolute; | |
| top: -50%; | |
| left: -50%; | |
| width: 200%; | |
| height: 200%; | |
| background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%); | |
| animation: pulse 4s ease-in-out infinite; | |
| } | |
| @keyframes pulse { | |
| 0%, 100% { transform: scale(1); opacity: 0.5; } | |
| 50% { transform: scale(1.1); opacity: 0.3; } | |
| } | |
| .header h1 { | |
| font-size: 2.5rem; | |
| font-weight: 700; | |
| margin-bottom: 10px; | |
| position: relative; | |
| z-index: 1; | |
| } | |
| .header p { | |
| font-size: 1.1rem; | |
| opacity: 0.9; | |
| position: relative; | |
| z-index: 1; | |
| } | |
| .form-container { | |
| padding: 40px; | |
| } | |
| .train-btn { | |
| width: 100%; | |
| padding: 16px; | |
| background: linear-gradient(135deg, #f093fb, #f5576c); | |
| color: white; | |
| border: none; | |
| border-radius: 12px; | |
| font-size: 1rem; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| text-transform: uppercase; | |
| letter-spacing: 1px; | |
| margin-bottom: 32px; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .train-btn:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 10px 25px rgba(240, 147, 251, 0.3); | |
| } | |
| .train-btn:disabled { | |
| opacity: 0.6; | |
| cursor: not-allowed; | |
| transform: none; | |
| } | |
| .train-btn::before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: -100%; | |
| width: 100%; | |
| height: 100%; | |
| background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent); | |
| transition: left 0.5s; | |
| } | |
| .train-btn:hover::before { | |
| left: 100%; | |
| } | |
| .form-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); | |
| gap: 24px; | |
| margin-bottom: 32px; | |
| } | |
| .form-group { | |
| position: relative; | |
| } | |
| .form-group label { | |
| display: block; | |
| font-weight: 600; | |
| color: #2d3748; | |
| margin-bottom: 8px; | |
| font-size: 0.9rem; | |
| text-transform: uppercase; | |
| letter-spacing: 0.5px; | |
| } | |
| .form-group input, | |
| .form-group select { | |
| width: 100%; | |
| padding: 16px 20px; | |
| border: 2px solid #e2e8f0; | |
| border-radius: 12px; | |
| font-size: 1rem; | |
| transition: all 0.3s ease; | |
| background: white; | |
| color: #2d3748; | |
| } | |
| .form-group input:focus, | |
| .form-group select:focus { | |
| outline: none; | |
| border-color: #667eea; | |
| box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.1); | |
| transform: translateY(-2px); | |
| } | |
| .form-group input:hover, | |
| .form-group select:hover { | |
| border-color: #cbd5e0; | |
| transform: translateY(-1px); | |
| } | |
| .form-group select { | |
| cursor: pointer; | |
| appearance: none; | |
| background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3e%3c/svg%3e"); | |
| background-position: right 12px center; | |
| background-repeat: no-repeat; | |
| background-size: 16px; | |
| } | |
| .submit-btn { | |
| width: 100%; | |
| padding: 18px; | |
| background: linear-gradient(135deg, #667eea, #764ba2); | |
| color: white; | |
| border: none; | |
| border-radius: 12px; | |
| font-size: 1.1rem; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| text-transform: uppercase; | |
| letter-spacing: 1px; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .submit-btn:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 10px 25px rgba(102, 126, 234, 0.3); | |
| } | |
| .submit-btn:active { | |
| transform: translateY(0); | |
| } | |
| .submit-btn::before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: -100%; | |
| width: 100%; | |
| height: 100%; | |
| background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent); | |
| transition: left 0.5s; | |
| } | |
| .submit-btn:hover::before { | |
| left: 100%; | |
| } | |
| .result { | |
| margin-top: 32px; | |
| padding: 24px; | |
| background: linear-gradient(135deg, #48bb78, #38a169); | |
| border-radius: 16px; | |
| color: white; | |
| text-align: center; | |
| opacity: 0; | |
| transform: translateY(20px); | |
| transition: all 0.5s ease; | |
| } | |
| .result.show { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| .error-result { | |
| margin-top: 32px; | |
| padding: 24px; | |
| background: linear-gradient(135deg, #e53e3e, #c53030); | |
| border-radius: 16px; | |
| color: white; | |
| text-align: center; | |
| opacity: 0; | |
| transform: translateY(20px); | |
| transition: all 0.5s ease; | |
| } | |
| .error-result.show { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| .result-icon { | |
| font-size: 3rem; | |
| margin-bottom: 16px; | |
| animation: bounce 2s infinite; | |
| } | |
| @keyframes bounce { | |
| 0%, 20%, 50%, 80%, 100% { transform: translateY(0); } | |
| 40% { transform: translateY(-10px); } | |
| 60% { transform: translateY(-5px); } | |
| } | |
| .result-text { | |
| font-size: 1.2rem; | |
| font-weight: 600; | |
| } | |
| .loading { | |
| display: none; | |
| justify-content: center; | |
| align-items: center; | |
| margin-top: 20px; | |
| padding: 20px; | |
| background: linear-gradient(135deg, #667eea, #764ba2); | |
| border-radius: 16px; | |
| color: white; | |
| } | |
| .loading.show { | |
| display: flex; | |
| } | |
| .spinner { | |
| width: 40px; | |
| height: 40px; | |
| border: 4px solid rgba(255, 255, 255, 0.3); | |
| border-top: 4px solid white; | |
| border-radius: 50%; | |
| animation: spin 1s linear infinite; | |
| margin-right: 12px; | |
| } | |
| @keyframes spin { | |
| 0% { transform: rotate(0deg); } | |
| 100% { transform: rotate(360deg); } | |
| } | |
| .feature-icons { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); | |
| gap: 20px; | |
| margin-top: 32px; | |
| padding-top: 32px; | |
| border-top: 2px solid #e2e8f0; | |
| } | |
| .feature-icon { | |
| text-align: center; | |
| padding: 20px; | |
| border-radius: 12px; | |
| background: linear-gradient(135deg, #f7fafc, #edf2f7); | |
| transition: transform 0.3s ease; | |
| } | |
| .feature-icon:hover { | |
| transform: translateY(-5px); | |
| } | |
| .feature-icon i { | |
| font-size: 2rem; | |
| color: #667eea; | |
| margin-bottom: 8px; | |
| } | |
| .feature-icon span { | |
| display: block; | |
| font-size: 0.8rem; | |
| color: #4a5568; | |
| font-weight: 500; | |
| } | |
| @media (max-width: 768px) { | |
| .form-grid { | |
| grid-template-columns: 1fr; | |
| gap: 20px; | |
| } | |
| .header h1 { | |
| font-size: 2rem; | |
| } | |
| .form-container { | |
| padding: 24px; | |
| } | |
| .header { | |
| padding: 32px 24px; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <div class="header"> | |
| <h1><i class="fas fa-motorcycle"></i> Food Delivery Time Prediction</h1> | |
| <p>Advanced AI-powered delivery time estimation system</p> | |
| </div> | |
| <div class="form-container"> | |
| <!-- Training Section --> | |
| <button type="button" class="train-btn" id="train-btn"> | |
| <i class="fas fa-graduation-cap"></i> Train Model | |
| </button> | |
| <form id="prediction-form"> | |
| <div class="form-grid"> | |
| <div class="form-group"> | |
| <label for="multiple_deliveries"><i class="fas fa-boxes"></i> Multiple Deliveries</label> | |
| <input type="number" id="multiple_deliveries" name="multiple_deliveries" step="1" min="0" placeholder="e.g., 1" required> | |
| </div> | |
| <div class="form-group"> | |
| <label for="Road_traffic_density"><i class="fas fa-road"></i> Road Traffic Density</label> | |
| <select id="Road_traffic_density" name="Road_traffic_density" required> | |
| <option value="" disabled selected>Select traffic level</option> | |
| <option value="Low">Low</option> | |
| <option value="Medium">Medium</option> | |
| <option value="High">High</option> | |
| <option value="Jam">Jam</option> | |
| </select> | |
| </div> | |
| <div class="form-group"> | |
| <label for="Vehicle_condition"><i class="fas fa-car"></i> Vehicle Condition</label> | |
| <select id="Vehicle_condition" name="Vehicle_condition" required> | |
| <option value="" disabled selected>Select condition</option> | |
| <option value="Poor">Poor</option> | |
| <option value="Good">Good</option> | |
| <option value="Excellent">Excellent</option> | |
| </select> | |
| </div> | |
| <div class="form-group"> | |
| <label for="Delivery_person_Ratings"><i class="fas fa-star"></i> Delivery Person Ratings (1-5)</label> | |
| <input type="number" id="Delivery_person_Ratings" name="Delivery_person_Ratings" step="0.1" min="1" max="5" placeholder="e.g., 4.5" required> | |
| </div> | |
| <div class="form-group"> | |
| <label for="distance_deliveries"><i class="fas fa-calculator"></i> Distance × Deliveries</label> | |
| <input type="number" id="distance_deliveries" name="distance_deliveries" step="0.1" placeholder="e.g., 10.5" required> | |
| </div> | |
| <div class="form-group"> | |
| <label for="Weather_conditions"><i class="fas fa-cloud-sun"></i> Weather Conditions</label> | |
| <select id="Weather_conditions" name="Weather_conditions" required> | |
| <option value="" disabled selected>Select weather</option> | |
| <option value="Sunny">Sunny</option> | |
| <option value="Cloudy">Cloudy</option> | |
| <option value="Fog">Fog</option> | |
| <option value="Sandstorms">Sandstorms</option> | |
| <option value="Stormy">Stormy</option> | |
| <option value="Windy">Windy</option> | |
| </select> | |
| </div> | |
| <div class="form-group"> | |
| <label for="Festival"><i class="fas fa-calendar-alt"></i> Festival</label> | |
| <select id="Festival" name="Festival" required> | |
| <option value="" disabled selected>Festival period?</option> | |
| <option value="No">No</option> | |
| <option value="Yes">Yes</option> | |
| </select> | |
| </div> | |
| <div class="form-group"> | |
| <label for="distance_traffic"><i class="fas fa-chart-line"></i> Distance × Traffic</label> | |
| <input type="number" id="distance_traffic" name="distance_traffic" step="0.1" placeholder="e.g., 15.2" required> | |
| </div> | |
| <div class="form-group"> | |
| <label for="distance"><i class="fas fa-route"></i> Distance (km)</label> | |
| <input type="number" id="distance" name="distance" step="0.1" min="0" placeholder="e.g., 5.0" required> | |
| </div> | |
| <div class="form-group"> | |
| <label for="Delivery_person_Age"><i class="fas fa-user"></i> Delivery Person Age</label> | |
| <input type="number" id="Delivery_person_Age" name="Delivery_person_Age" step="1" min="18" max="60" placeholder="e.g., 30" required> | |
| </div> | |
| <div class="form-group"> | |
| <label for="prep_traffic"><i class="fas fa-clock"></i> Prep Time × Traffic</label> | |
| <input type="number" id="prep_traffic" name="prep_traffic" step="0.1" placeholder="e.g., 12.0" required> | |
| </div> | |
| <div class="form-group"> | |
| <label for="City"><i class="fas fa-city"></i> City</label> | |
| <select id="City" name="City" required> | |
| <option value="" disabled selected>Select city type</option> | |
| <option value="Urban">Urban</option> | |
| <option value="Semi-Urban">Semi-Urban</option> | |
| <option value="Metropolitan">Metropolitan</option> | |
| </select> | |
| </div> | |
| </div> | |
| <button type="submit" class="submit-btn" id="predict-btn"> | |
| <i class="fas fa-magic"></i> Predict Delivery Time | |
| </button> | |
| </form> | |
| <div class="loading" id="loading"> | |
| <div class="spinner"></div> | |
| <span id="loading-text">Processing...</span> | |
| </div> | |
| <div id="result" class="result"> | |
| <div class="result-icon">🚀</div> | |
| <div class="result-text"></div> | |
| </div> | |
| <div id="error-result" class="error-result"> | |
| <div class="result-icon">❌</div> | |
| <div class="result-text"></div> | |
| </div> | |
| <div class="feature-icons"> | |
| <div class="feature-icon"> | |
| <i class="fas fa-brain"></i> | |
| <span>AI Powered</span> | |
| </div> | |
| <div class="feature-icon"> | |
| <i class="fas fa-tachometer-alt"></i> | |
| <span>Real-time</span> | |
| </div> | |
| <div class="feature-icon"> | |
| <i class="fas fa-chart-bar"></i> | |
| <span>Accurate</span> | |
| </div> | |
| <div class="feature-icon"> | |
| <i class="fas fa-mobile-alt"></i> | |
| <span>Responsive</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Helper function to show loading | |
| function showLoading(message = "Processing...") { | |
| document.getElementById('loading-text').textContent = message; | |
| document.getElementById('loading').classList.add('show'); | |
| document.getElementById('result').classList.remove('show'); | |
| document.getElementById('error-result').classList.remove('show'); | |
| } | |
| // Helper function to hide loading | |
| function hideLoading() { | |
| document.getElementById('loading').classList.remove('show'); | |
| } | |
| // Helper function to show success result | |
| function showSuccess(message) { | |
| document.getElementById('result').classList.add('show'); | |
| document.querySelector('#result .result-text').innerHTML = message; | |
| document.getElementById('result').scrollIntoView({ | |
| behavior: 'smooth', | |
| block: 'center' | |
| }); | |
| } | |
| // Helper function to show error result | |
| function showError(message) { | |
| document.getElementById('error-result').classList.add('show'); | |
| document.querySelector('#error-result .result-text').innerHTML = message; | |
| document.getElementById('error-result').scrollIntoView({ | |
| behavior: 'smooth', | |
| block: 'center' | |
| }); | |
| } | |
| // Training functionality - ONLY when train button is clicked | |
| document.getElementById('train-btn').addEventListener('click', async function() { | |
| const trainBtn = this; | |
| // Show loading and disable button | |
| showLoading("Training model with latest data..."); | |
| trainBtn.disabled = true; | |
| trainBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Training in Progress...'; | |
| try { | |
| const response = await fetch('/train', { | |
| method: 'GET', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| } | |
| }); | |
| hideLoading(); | |
| if (response.ok) { | |
| const text = await response.text(); | |
| showSuccess( | |
| `<strong>✅ Model Training Completed Successfully!</strong><br> | |
| <small>The AI model has been retrained with the latest data and is ready for predictions</small>` | |
| ); | |
| } else { | |
| const errorText = await response.text(); | |
| throw new Error(`Training failed: ${response.status} - ${errorText}`); | |
| } | |
| } catch (error) { | |
| console.error('Training error:', error); | |
| hideLoading(); | |
| showError( | |
| `<strong>Training Failed!</strong><br> | |
| <small>Error: ${error.message}<br> | |
| Please check your server connection and try again.</small>` | |
| ); | |
| } finally { | |
| // Re-enable button | |
| trainBtn.disabled = false; | |
| trainBtn.innerHTML = '<i class="fas fa-graduation-cap"></i> Train Model'; | |
| } | |
| }); | |
| // Prediction functionality - when form is submitted | |
| document.getElementById('prediction-form').addEventListener('submit', async function(e) { | |
| e.preventDefault(); | |
| const predictBtn = document.getElementById('predict-btn'); | |
| // Show loading and disable predict button | |
| showLoading("Calculating delivery time..."); | |
| predictBtn.disabled = true; | |
| predictBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Predicting...'; | |
| // Collect form data | |
| const formData = new FormData(this); | |
| try { | |
| const response = await fetch('/predict', { | |
| method: 'POST', | |
| body: formData | |
| }); | |
| const data = await response.json(); | |
| hideLoading(); | |
| if (response.ok && data.prediction !== undefined) { | |
| // Show successful prediction result | |
| showSuccess( | |
| `<strong>Estimated Delivery Time: ${data.prediction} minutes</strong><br> | |
| <small>Based on current conditions and AI analysis</small>` | |
| ); | |
| } else { | |
| // Show error from backend | |
| throw new Error(data.error || 'Prediction failed'); | |
| } | |
| } catch (error) { | |
| console.error('Prediction error:', error); | |
| hideLoading(); | |
| showError( | |
| `<strong>Prediction Failed!</strong><br> | |
| <small>Error: ${error.message}<br> | |
| Please check your input values and try again.</small>` | |
| ); | |
| } finally { | |
| // Re-enable predict button | |
| predictBtn.disabled = false; | |
| predictBtn.innerHTML = '<i class="fas fa-magic"></i> Predict Delivery Time'; | |
| } | |
| }); | |
| // Add input animations | |
| document.querySelectorAll('input, select').forEach(element => { | |
| element.addEventListener('focus', function() { | |
| this.parentElement.style.transform = 'scale(1.02)'; | |
| }); | |
| element.addEventListener('blur', function() { | |
| this.parentElement.style.transform = 'scale(1)'; | |
| }); | |
| }); | |
| // Add form validation feedback | |
| document.querySelectorAll('input[required], select[required]').forEach(element => { | |
| element.addEventListener('invalid', function() { | |
| this.style.borderColor = '#e53e3e'; | |
| this.style.boxShadow = '0 0 0 4px rgba(229, 62, 62, 0.1)'; | |
| }); | |
| element.addEventListener('input', function() { | |
| if (this.validity.valid) { | |
| this.style.borderColor = '#48bb78'; | |
| this.style.boxShadow = '0 0 0 4px rgba(72, 187, 120, 0.1)'; | |
| } else { | |
| this.style.borderColor = '#e2e8f0'; | |
| this.style.boxShadow = 'none'; | |
| } | |
| }); | |
| }); | |
| // Clear results when form is modified | |
| document.querySelectorAll('input, select').forEach(element => { | |
| element.addEventListener('input', function() { | |
| document.getElementById('result').classList.remove('show'); | |
| document.getElementById('error-result').classList.remove('show'); | |
| }); | |
| }); | |
| </script> | |
| </body> | |
| </html> |