Spaces:
Sleeping
Sleeping
| // Flight Delay Prediction System - JavaScript | |
| // Smooth scrolling | |
| function scrollToSection(sectionId) { | |
| const element = document.getElementById(sectionId); | |
| if (element) { | |
| element.scrollIntoView({ behavior: 'smooth' }); | |
| } | |
| } | |
| // Initialize charts | |
| let hourlyChart, seasonalChart, featureImportanceChart, predictionTrendsChart, airlinePerformanceChart, airportPerformanceChart; | |
| // Load insights data | |
| async function loadInsights() { | |
| try { | |
| const response = await fetch('/api/insights'); | |
| const data = await response.json(); | |
| if (data.error) { | |
| console.error('Error loading insights:', data.error); | |
| return; | |
| } | |
| // Update metrics | |
| document.getElementById('delay-rate').textContent = data.delay_rate + '%'; | |
| document.getElementById('total-flights').textContent = data.total_flights.toLocaleString(); | |
| document.getElementById('avg-delay').textContent = data.avg_delay; | |
| // Create charts with real data | |
| await createHourlyChart(); | |
| await createSeasonalChart(); | |
| await createFeatureImportanceChart(); | |
| await createPredictionTrendsChart(); | |
| await createAirlinePerformanceChart(); | |
| await createAirportPerformanceChart(); | |
| } catch (error) { | |
| console.error('Error fetching insights:', error); | |
| } | |
| } | |
| // Create hourly delays chart | |
| async function createHourlyChart() { | |
| try { | |
| const response = await fetch('/api/chart-data/hourly'); | |
| const data = await response.json(); | |
| if (data.error) { | |
| console.error('Error loading hourly data:', data.error); | |
| return; | |
| } | |
| const ctx = document.getElementById('hourly-chart').getContext('2d'); | |
| if (hourlyChart) { | |
| hourlyChart.destroy(); | |
| } | |
| hourlyChart = new Chart(ctx, { | |
| type: 'bar', | |
| data: { | |
| labels: data.labels.map(h => h + ':00'), | |
| datasets: [{ | |
| label: 'Delay Rate by Hour', | |
| data: data.data, | |
| backgroundColor: '#2F80ED', | |
| borderColor: '#2F80ED', | |
| borderWidth: 1, | |
| borderRadius: 4 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { | |
| display: false | |
| }, | |
| title: { | |
| display: true, | |
| text: 'Delays by Departure Hour', | |
| color: '#1A2B3C', | |
| font: { | |
| size: 16, | |
| weight: 'bold', | |
| family: 'Inter' | |
| } | |
| } | |
| }, | |
| scales: { | |
| y: { | |
| beginAtZero: true, | |
| title: { | |
| display: true, | |
| text: 'Delay Rate (%)', | |
| color: '#4A5D73', | |
| font: { | |
| family: 'Inter' | |
| } | |
| }, | |
| ticks: { | |
| color: '#4A5D73', | |
| font: { | |
| family: 'Inter' | |
| } | |
| }, | |
| grid: { | |
| color: 'rgba(74, 93, 115, 0.1)' | |
| } | |
| }, | |
| x: { | |
| title: { | |
| display: true, | |
| text: 'Hour of Day', | |
| color: '#4A5D73', | |
| font: { | |
| family: 'Inter' | |
| } | |
| }, | |
| ticks: { | |
| color: '#4A5D73', | |
| font: { | |
| family: 'Inter' | |
| } | |
| }, | |
| grid: { | |
| color: 'rgba(74, 93, 115, 0.1)' | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| } catch (error) { | |
| console.error('Error creating hourly chart:', error); | |
| } | |
| } | |
| // Create seasonal delays chart | |
| async function createSeasonalChart() { | |
| try { | |
| const response = await fetch('/api/chart-data/seasonal'); | |
| const data = await response.json(); | |
| if (data.error) { | |
| console.error('Error loading seasonal data:', data.error); | |
| return; | |
| } | |
| const ctx = document.getElementById('seasonal-chart').getContext('2d'); | |
| if (seasonalChart) { | |
| seasonalChart.destroy(); | |
| } | |
| seasonalChart = new Chart(ctx, { | |
| type: 'pie', | |
| data: { | |
| labels: data.labels, | |
| datasets: [{ | |
| data: data.data, | |
| backgroundColor: ['#2F80ED', '#27AE60', '#56CCF2', '#F2994A'], | |
| borderWidth: 2, | |
| borderColor: '#fff', | |
| borderRadius: 4 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { | |
| position: 'bottom', | |
| labels: { | |
| color: '#1A2B3C', | |
| padding: 20, | |
| font: { | |
| family: 'Inter' | |
| } | |
| } | |
| }, | |
| title: { | |
| display: true, | |
| text: 'Delays by Season', | |
| color: '#1A2B3C', | |
| font: { | |
| size: 16, | |
| weight: 'bold', | |
| family: 'Inter' | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| } catch (error) { | |
| console.error('Error creating seasonal chart:', error); | |
| } | |
| } | |
| // Create feature importance chart | |
| async function createFeatureImportanceChart() { | |
| try { | |
| const response = await fetch('/api/chart-data/feature-importance'); | |
| const data = await response.json(); | |
| if (data.error) { | |
| console.error('Error loading feature importance data:', data.error); | |
| return; | |
| } | |
| const ctx = document.getElementById('feature-importance-chart').getContext('2d'); | |
| if (featureImportanceChart) { | |
| featureImportanceChart.destroy(); | |
| } | |
| featureImportanceChart = new Chart(ctx, { | |
| type: 'bar', | |
| data: { | |
| labels: data.labels, | |
| datasets: [{ | |
| label: 'Feature Importance', | |
| data: data.data, | |
| backgroundColor: '#2F80ED', | |
| borderColor: '#2F80ED', | |
| borderWidth: 1, | |
| borderRadius: 4 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| indexAxis: 'y', | |
| plugins: { | |
| legend: { | |
| display: false | |
| }, | |
| title: { | |
| display: true, | |
| text: 'Feature Importance', | |
| color: '#1A2B3C', | |
| font: { | |
| size: 16, | |
| weight: 'bold', | |
| family: 'Inter' | |
| } | |
| } | |
| }, | |
| scales: { | |
| x: { | |
| title: { | |
| display: true, | |
| text: 'Importance', | |
| color: '#4A5D73', | |
| font: { | |
| family: 'Inter' | |
| } | |
| }, | |
| ticks: { | |
| color: '#4A5D73', | |
| font: { | |
| family: 'Inter' | |
| } | |
| }, | |
| grid: { | |
| color: 'rgba(74, 93, 115, 0.1)' | |
| } | |
| }, | |
| y: { | |
| ticks: { | |
| color: '#4A5D73', | |
| font: { | |
| family: 'Inter' | |
| } | |
| }, | |
| grid: { | |
| color: 'rgba(74, 93, 115, 0.1)' | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| } catch (error) { | |
| console.error('Error creating feature importance chart:', error); | |
| } | |
| } | |
| // Create prediction trends chart | |
| async function createPredictionTrendsChart() { | |
| try { | |
| const response = await fetch('/api/chart-data/prediction-trends'); | |
| const data = await response.json(); | |
| if (data.error) { | |
| console.error('Error loading prediction trends data:', data.error); | |
| return; | |
| } | |
| const ctx = document.getElementById('prediction-trends-chart').getContext('2d'); | |
| if (predictionTrendsChart) { | |
| predictionTrendsChart.destroy(); | |
| } | |
| predictionTrendsChart = new Chart(ctx, { | |
| type: 'line', | |
| data: { | |
| labels: data.labels, | |
| datasets: [{ | |
| label: 'Prediction Confidence', | |
| data: data.data, | |
| borderColor: '#2F80ED', | |
| backgroundColor: 'rgba(47, 128, 237, 0.1)', | |
| borderWidth: 3, | |
| fill: true, | |
| tension: 0.4, | |
| pointBackgroundColor: '#2F80ED', | |
| pointBorderColor: '#fff', | |
| pointBorderWidth: 2, | |
| pointRadius: 5, | |
| pointHoverRadius: 7 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { | |
| display: false | |
| }, | |
| title: { | |
| display: true, | |
| text: 'Prediction Trends Over Time', | |
| color: '#1A2B3C', | |
| font: { | |
| size: 16, | |
| weight: 'bold', | |
| family: 'Inter' | |
| } | |
| } | |
| }, | |
| scales: { | |
| y: { | |
| beginAtZero: true, | |
| max: 100, | |
| title: { | |
| display: true, | |
| text: 'Confidence (%)', | |
| color: '#4A5D73', | |
| font: { | |
| family: 'Inter' | |
| } | |
| }, | |
| ticks: { | |
| color: '#4A5D73', | |
| font: { | |
| family: 'Inter' | |
| } | |
| }, | |
| grid: { | |
| color: 'rgba(74, 93, 115, 0.1)' | |
| } | |
| }, | |
| x: { | |
| title: { | |
| display: true, | |
| text: 'Day', | |
| color: '#4A5D73', | |
| font: { | |
| family: 'Inter' | |
| } | |
| }, | |
| ticks: { | |
| color: '#4A5D73', | |
| font: { | |
| family: 'Inter' | |
| } | |
| }, | |
| grid: { | |
| color: 'rgba(74, 93, 115, 0.1)' | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| } catch (error) { | |
| console.error('Error creating prediction trends chart:', error); | |
| } | |
| } | |
| // Create airline performance chart | |
| async function createAirlinePerformanceChart() { | |
| try { | |
| const response = await fetch('/api/chart-data/airline-performance'); | |
| const data = await response.json(); | |
| if (data.error) { | |
| console.error('Error loading airline performance data:', data.error); | |
| return; | |
| } | |
| const ctx = document.getElementById('airline-performance-chart').getContext('2d'); | |
| if (airlinePerformanceChart) { | |
| airlinePerformanceChart.destroy(); | |
| } | |
| airlinePerformanceChart = new Chart(ctx, { | |
| type: 'bar', | |
| data: { | |
| labels: data.labels, | |
| datasets: [{ | |
| label: 'Delay Rate by Airline', | |
| data: data.data, | |
| backgroundColor: '#2F80ED', | |
| borderColor: '#2F80ED', | |
| borderWidth: 1, | |
| borderRadius: 4 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { | |
| display: false | |
| }, | |
| title: { | |
| display: true, | |
| text: 'Airline Performance', | |
| color: '#1A2B3C', | |
| font: { | |
| size: 16, | |
| weight: 'bold', | |
| family: 'Inter' | |
| } | |
| } | |
| }, | |
| scales: { | |
| y: { | |
| beginAtZero: true, | |
| title: { | |
| display: true, | |
| text: 'Delay Rate (%)', | |
| color: '#4A5D73', | |
| font: { | |
| family: 'Inter' | |
| } | |
| }, | |
| ticks: { | |
| color: '#4A5D73', | |
| font: { | |
| family: 'Inter' | |
| } | |
| }, | |
| grid: { | |
| color: 'rgba(74, 93, 115, 0.1)' | |
| } | |
| }, | |
| x: { | |
| title: { | |
| display: true, | |
| text: 'Airlines', | |
| color: '#4A5D73', | |
| font: { | |
| family: 'Inter', | |
| size: 14 | |
| } | |
| }, | |
| ticks: { | |
| color: '#4A5D73', | |
| font: { | |
| family: 'Inter', | |
| size: 11 | |
| }, | |
| maxRotation: 45, | |
| minRotation: 0, | |
| autoSkip: false, | |
| callback: function(value, index, values) { | |
| const label = this.getLabelForValue(value); | |
| if (label.length > 20) { | |
| return label.substring(0, 18) + '...'; | |
| } | |
| return label; | |
| } | |
| }, | |
| grid: { | |
| color: 'rgba(74, 93, 115, 0.1)' | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| } catch (error) { | |
| console.error('Error creating airline performance chart:', error); | |
| } | |
| } | |
| // Create airport performance chart | |
| async function createAirportPerformanceChart() { | |
| try { | |
| const response = await fetch('/api/chart-data/airport-performance'); | |
| const data = await response.json(); | |
| if (data.error) { | |
| console.error('Error loading airport performance data:', data.error); | |
| return; | |
| } | |
| const ctx = document.getElementById('airport-performance-chart').getContext('2d'); | |
| if (airportPerformanceChart) { | |
| airportPerformanceChart.destroy(); | |
| } | |
| // Prepare data for dual-axis chart | |
| const airports = Object.keys(data.top_airports); | |
| const delayRates = Object.values(data.delay_rates); | |
| const flightVolumes = Object.values(data.top_airports); | |
| airportPerformanceChart = new Chart(ctx, { | |
| type: 'bar', | |
| data: { | |
| labels: airports, | |
| datasets: [ | |
| { | |
| label: 'Delay Rate (%)', | |
| data: delayRates, | |
| backgroundColor: '#EB5757', | |
| borderColor: '#EB5757', | |
| borderWidth: 1, | |
| borderRadius: 4, | |
| yAxisID: 'y' | |
| }, | |
| { | |
| label: 'Flight Volume', | |
| data: flightVolumes, | |
| backgroundColor: '#2F80ED', | |
| borderColor: '#2F80ED', | |
| borderWidth: 1, | |
| borderRadius: 4, | |
| yAxisID: 'y1' | |
| } | |
| ] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { | |
| display: true, | |
| position: 'bottom', | |
| labels: { | |
| color: '#1A2B3C', | |
| padding: 20, | |
| font: { | |
| family: 'Inter' | |
| } | |
| } | |
| }, | |
| title: { | |
| display: true, | |
| text: 'Airport Performance', | |
| color: '#1A2B3C', | |
| font: { | |
| size: 16, | |
| weight: 'bold', | |
| family: 'Inter' | |
| } | |
| } | |
| }, | |
| scales: { | |
| y: { | |
| type: 'linear', | |
| display: true, | |
| title: { | |
| display: true, | |
| text: 'Delay Rate (%)', | |
| color: '#4A5D73', | |
| font: { | |
| family: 'Inter' | |
| } | |
| }, | |
| ticks: { | |
| color: '#4A5D73', | |
| font: { | |
| family: 'Inter' | |
| } | |
| }, | |
| grid: { | |
| color: 'rgba(74, 93, 115, 0.1)' | |
| } | |
| }, | |
| y1: { | |
| type: 'linear', | |
| position: 'right', | |
| title: { | |
| display: true, | |
| text: 'Flight Volume', | |
| color: '#4A5D73', | |
| font: { | |
| family: 'Inter' | |
| } | |
| }, | |
| ticks: { | |
| color: '#4A5D73', | |
| font: { | |
| family: 'Inter' | |
| } | |
| }, | |
| grid: { | |
| display: false | |
| } | |
| }, | |
| x: { | |
| title: { | |
| display: true, | |
| text: 'Airports', | |
| color: '#4A5D73', | |
| font: { | |
| family: 'Inter', | |
| size: 14 | |
| } | |
| }, | |
| ticks: { | |
| color: '#4A5D73', | |
| font: { | |
| family: 'Inter', | |
| size: 11 | |
| }, | |
| maxRotation: 45, | |
| minRotation: 0, | |
| autoSkip: false, | |
| callback: function(value, index, values) { | |
| const label = this.getLabelForValue(value); | |
| if (label.length > 25) { | |
| return label.substring(0, 23) + '...'; | |
| } | |
| return label; | |
| } | |
| }, | |
| grid: { | |
| color: 'rgba(74, 93, 115, 0.1)' | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| } catch (error) { | |
| console.error('Error creating airport performance chart:', error); | |
| } | |
| } | |
| // Create prediction chart for specific prediction | |
| async function createPredictionChart(predictionData) { | |
| const ctx = document.getElementById('prediction-chart').getContext('2d'); | |
| if (window.predictionChart) { | |
| window.predictionChart.destroy(); | |
| } | |
| // Create gauge chart for prediction | |
| const prediction = predictionData.prediction; | |
| const probability = predictionData.probability; | |
| window.predictionChart = new Chart(ctx, { | |
| type: 'doughnut', | |
| data: { | |
| labels: ['On Time', 'Delayed'], | |
| datasets: [{ | |
| data: [prediction === 0 ? 100 - probability : probability, prediction === 1 ? probability : 100 - probability], | |
| backgroundColor: ['#27AE60', '#EB5757'], | |
| borderWidth: 2, | |
| borderColor: '#fff', | |
| borderRadius: 4 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| cutout: '70%', | |
| plugins: { | |
| legend: { | |
| display: true, | |
| position: 'bottom', | |
| labels: { | |
| color: '#1A2B3C', | |
| padding: 20, | |
| font: { | |
| family: 'Inter' | |
| } | |
| } | |
| }, | |
| title: { | |
| display: true, | |
| text: `Flight Prediction - ${predictionData.status}`, | |
| color: '#1A2B3C', | |
| font: { | |
| size: 16, | |
| weight: 'bold', | |
| family: 'Inter' | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| // Load airlines and airports for form | |
| async function loadFormData() { | |
| try { | |
| // Load airlines | |
| const airlinesResponse = await fetch('/api/airlines'); | |
| const airlinesData = await airlinesResponse.json(); | |
| const airlineSelect = document.getElementById('airline'); | |
| if (airlinesData.airlines && !airlinesData.error) { | |
| airlinesData.airlines.forEach(airline => { | |
| const option = document.createElement('option'); | |
| option.value = airline; | |
| option.textContent = airline; | |
| airlineSelect.appendChild(option); | |
| }); | |
| } | |
| // Load airports | |
| const airportsResponse = await fetch('/api/airports'); | |
| const airportsData = await airportsResponse.json(); | |
| const originSelect = document.getElementById('origin'); | |
| const destinationSelect = document.getElementById('destination'); | |
| if (airportsData.airports && !airportsData.error) { | |
| airportsData.airports.forEach(airport => { | |
| const option1 = document.createElement('option'); | |
| option1.value = airport; | |
| option1.textContent = airport; | |
| originSelect.appendChild(option1); | |
| const option2 = document.createElement('option'); | |
| option2.value = airport; | |
| option2.textContent = airport; | |
| destinationSelect.appendChild(option2); | |
| }); | |
| } | |
| // Add event listeners for automatic season calculation | |
| const flightDateInput = document.getElementById('flight_date'); | |
| const originAirportSelect = document.getElementById('origin'); | |
| const seasonInput = document.getElementById('season'); | |
| // Function to update season based on date and origin | |
| function updateSeason() { | |
| const flightDate = flightDateInput.value; | |
| const origin = originAirportSelect.value; | |
| if (flightDate && origin) { | |
| // Extract origin airport code for season calculation | |
| const originCode = origin.split('(').pop()?.replace(')', '').trim() || origin.split()[0]; | |
| // Calculate season based on date and origin | |
| const season = calculateSeasonFromDate(flightDate, originCode); | |
| seasonInput.value = season; | |
| } | |
| } | |
| flightDateInput.addEventListener('change', updateSeason); | |
| originAirportSelect.addEventListener('change', updateSeason); | |
| // Set default date to today | |
| const today = new Date().toISOString().split('T')[0]; | |
| flightDateInput.value = today; | |
| } catch (error) { | |
| console.error('Error loading form data:', error); | |
| } | |
| } | |
| // Calculate season based on date and origin airport | |
| function calculateSeasonFromDate(dateStr, originAirport) { | |
| try { | |
| const date = new Date(dateStr); | |
| const month = date.getMonth() + 1; // 1-12 | |
| // Major southern hemisphere airports | |
| const southernAirports = ['SYD', 'MEL', 'BNE', 'ADL', 'PER', 'CBR', 'HBA', 'DRW', 'CNS', 'OOL']; | |
| // Extract airport code | |
| const originCode = originAirport.split('(').pop()?.replace(')', '').trim() || originAirport.split()[0]; | |
| const isSouthern = southernAirports.includes(originCode.toUpperCase()); | |
| if (isSouthern) { | |
| // Southern hemisphere seasons are reversed | |
| if ([12, 1, 2].includes(month)) return 'Summer'; | |
| if ([3, 4, 5].includes(month)) return 'Fall'; | |
| if ([6, 7, 8].includes(month)) return 'Winter'; | |
| return 'Spring'; // [9, 10, 11] | |
| } else { | |
| // Northern hemisphere seasons | |
| if ([12, 1, 2].includes(month)) return 'Winter'; | |
| if ([3, 4, 5].includes(month)) return 'Spring'; | |
| if ([6, 7, 8].includes(month)) return 'Summer'; | |
| return 'Fall'; // [9, 10, 11] | |
| } | |
| } catch (error) { | |
| console.error('Error calculating season:', error); | |
| return 'Spring'; | |
| } | |
| } | |
| // Handle prediction form submission | |
| document.getElementById('prediction-form').addEventListener('submit', async function(e) { | |
| e.preventDefault(); | |
| console.log('=== FORM SUBMISSION STARTED ==='); | |
| const submitButton = this.querySelector('button[type="submit"]'); | |
| const originalText = submitButton.textContent; | |
| // Get form values directly | |
| const airline = document.getElementById('airline').value; | |
| const origin = document.getElementById('origin').value; | |
| const destination = document.getElementById('destination').value; | |
| const departure_hour = document.getElementById('departure_hour').value; | |
| const flight_date = document.getElementById('flight_date').value; | |
| console.log('Form values:', { airline, origin, destination, departure_hour, flight_date }); | |
| // Validate form data | |
| if (!airline || !origin || !destination || !departure_hour || !flight_date) { | |
| alert('Please fill in all required fields.'); | |
| return; | |
| } | |
| // Show loading state | |
| submitButton.textContent = 'Predicting...'; | |
| submitButton.disabled = true; | |
| try { | |
| // Create FormData exactly like the working test function | |
| const formData = new FormData(); | |
| formData.append('airline', airline); | |
| formData.append('origin', origin); | |
| formData.append('destination', destination); | |
| formData.append('departure_hour', departure_hour); | |
| formData.append('flight_date', flight_date); | |
| console.log('Sending prediction request...'); | |
| const response = await fetch('/api/predict', { | |
| method: 'POST', | |
| body: formData | |
| }); | |
| console.log('Response status:', response.status); | |
| if (!response.ok) { | |
| const errorText = await response.text(); | |
| console.error('Server error:', errorText); | |
| throw new Error(`Server error: ${response.status} - ${errorText}`); | |
| } | |
| const result = await response.json(); | |
| console.log('Prediction result:', result); | |
| if (result.error) { | |
| alert('Error: ' + result.error); | |
| return; | |
| } | |
| // Display result directly on webpage | |
| displayPredictionResult(result); | |
| // Create prediction chart | |
| try { | |
| await createPredictionChart(result); | |
| document.getElementById('prediction-chart-container').style.display = 'block'; | |
| } catch (chartError) { | |
| console.error('Error creating prediction chart:', chartError); | |
| } | |
| // Create flight visualization chart | |
| try { | |
| await createFlightVisualizationChart(result); | |
| document.getElementById('flight-visualization-container').style.display = 'block'; | |
| } catch (flightChartError) { | |
| console.error('Error creating flight visualization chart:', flightChartError); | |
| } | |
| console.log('=== FORM SUBMISSION COMPLETED ==='); | |
| } catch (error) { | |
| console.error('Error making prediction:', error); | |
| alert('Error making prediction: ' + error.message + '. Please try again.'); | |
| } finally { | |
| // Reset button | |
| submitButton.textContent = originalText; | |
| submitButton.disabled = false; | |
| } | |
| }); | |
| // Display prediction result | |
| function displayPredictionResult(result) { | |
| try { | |
| console.log('Displaying prediction result:', result); | |
| const resultDiv = document.getElementById('prediction-result'); | |
| const probabilityDiv = document.getElementById('result-probability'); | |
| const statusDiv = document.getElementById('result-status'); | |
| const descriptionDiv = document.getElementById('result-description'); | |
| if (!resultDiv || !probabilityDiv || !statusDiv || !descriptionDiv) { | |
| console.error('Result display elements not found:', { | |
| resultDiv: !!resultDiv, | |
| probabilityDiv: !!probabilityDiv, | |
| statusDiv: !!statusDiv, | |
| descriptionDiv: !!descriptionDiv | |
| }); | |
| return; | |
| } | |
| // Update result display | |
| probabilityDiv.textContent = (result.probability || 0) + '%'; | |
| statusDiv.textContent = result.status || 'Unknown'; | |
| // Set color based on prediction | |
| if (result.prediction === 1) { | |
| probabilityDiv.className = 'result-probability result-delayed'; | |
| statusDiv.className = 'result-label result-delayed'; | |
| descriptionDiv.textContent = 'High probability of delay. Consider alternative flights or allow extra time.'; | |
| } else { | |
| probabilityDiv.className = 'result-probability result-ontime'; | |
| statusDiv.className = 'result-label result-ontime'; | |
| descriptionDiv.textContent = 'Low probability of delay. Flight is likely to be on time.'; | |
| } | |
| // Show result section | |
| resultDiv.style.display = 'block'; | |
| console.log('Result displayed successfully'); | |
| // Scroll to result | |
| setTimeout(() => { | |
| resultDiv.scrollIntoView({ behavior: 'smooth', block: 'center' }); | |
| }, 100); | |
| } catch (error) { | |
| console.error('Error displaying prediction result:', error); | |
| } | |
| } | |
| // Create flight visualization chart | |
| async function createFlightVisualizationChart(predictionData) { | |
| try { | |
| const response = await fetch('/api/flight-visualization', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/x-www-form-urlencoded', | |
| }, | |
| body: new URLSearchParams({ | |
| airline: predictionData.airline, | |
| origin: predictionData.origin, | |
| destination: predictionData.destination, | |
| probability: predictionData.probability, | |
| season: predictionData.season | |
| }) | |
| }); | |
| const data = await response.json(); | |
| if (data.error) { | |
| console.error('Error loading flight visualization data:', data.error); | |
| return; | |
| } | |
| const ctx = document.getElementById('flight-visualization-chart').getContext('2d'); | |
| if (window.flightVisualizationChart) { | |
| window.flightVisualizationChart.destroy(); | |
| } | |
| // Create combination chart with flight phases and risk factors | |
| window.flightVisualizationChart = new Chart(ctx, { | |
| type: 'bar', | |
| data: { | |
| labels: data.flight_phases, | |
| datasets: [ | |
| { | |
| label: 'Confidence Level', | |
| data: data.phase_confidences, | |
| backgroundColor: 'rgba(47, 128, 237, 0.8)', | |
| borderColor: '#2F80ED', | |
| borderWidth: 2, | |
| borderRadius: 4, | |
| yAxisID: 'y' | |
| } | |
| ] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { | |
| display: true, | |
| position: 'top', | |
| labels: { | |
| color: '#1A2B3C', | |
| font: { | |
| family: 'Inter' | |
| } | |
| } | |
| }, | |
| title: { | |
| display: true, | |
| text: `Flight Analysis: ${data.airline} - ${data.origin} to ${data.destination}`, | |
| color: '#1A2B3C', | |
| font: { | |
| size: 16, | |
| weight: 'bold', | |
| family: 'Inter' | |
| } | |
| }, | |
| subtitle: { | |
| display: true, | |
| text: `Season: ${data.season} | Overall Delay Probability: ${data.overall_probability}%`, | |
| color: '#4A5D73', | |
| font: { | |
| size: 14, | |
| family: 'Inter' | |
| } | |
| } | |
| }, | |
| scales: { | |
| y: { | |
| type: 'linear', | |
| display: true, | |
| position: 'left', | |
| title: { | |
| display: true, | |
| text: 'Confidence (%)', | |
| color: '#4A5D73', | |
| font: { | |
| family: 'Inter' | |
| } | |
| }, | |
| ticks: { | |
| color: '#4A5D73', | |
| font: { | |
| family: 'Inter' | |
| } | |
| }, | |
| grid: { | |
| color: 'rgba(74, 93, 115, 0.1)' | |
| }, | |
| max: 100 | |
| }, | |
| x: { | |
| title: { | |
| display: true, | |
| text: 'Flight Phases', | |
| color: '#4A5D73', | |
| font: { | |
| family: 'Inter' | |
| } | |
| }, | |
| ticks: { | |
| color: '#4A5D73', | |
| font: { | |
| family: 'Inter' | |
| } | |
| }, | |
| grid: { | |
| color: 'rgba(74, 93, 115, 0.1)' | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| // Create risk factors chart below | |
| createRiskFactorsChart(data); | |
| } catch (error) { | |
| console.error('Error creating flight visualization chart:', error); | |
| } | |
| } | |
| // Create risk factors chart | |
| function createRiskFactorsChart(data) { | |
| // Create a second chart for risk factors if it doesn't exist | |
| const riskContainer = document.getElementById('flight-visualization-container'); | |
| // Check if risk chart already exists | |
| if (document.getElementById('risk-factors-chart')) { | |
| return; | |
| } | |
| // Create risk factors chart container | |
| const riskChartDiv = document.createElement('div'); | |
| riskChartDiv.className = 'dashboard-card chart-container'; | |
| riskChartDiv.style.gridColumn = '1 / -1'; | |
| riskChartDiv.style.marginTop = '30px'; | |
| riskChartDiv.innerHTML = '<canvas id="risk-factors-chart"></canvas>'; | |
| riskContainer.appendChild(riskChartDiv); | |
| const ctx = document.getElementById('risk-factors-chart').getContext('2d'); | |
| if (window.riskFactorsChart) { | |
| window.riskFactorsChart.destroy(); | |
| } | |
| window.riskFactorsChart = new Chart(ctx, { | |
| type: 'doughnut', | |
| data: { | |
| labels: data.risk_factors, | |
| datasets: [{ | |
| data: data.risk_values, | |
| backgroundColor: [ | |
| '#EB5757', // Weather Risk - Red | |
| '#F2994A', // Traffic Congestion - Orange | |
| '#56CCF2', // Airport Delay History - Light Blue | |
| '#27AE60', // Airline Performance - Green | |
| '#2F80ED' // Time of Day - Blue | |
| ], | |
| borderWidth: 2, | |
| borderColor: '#fff', | |
| borderRadius: 4 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { | |
| display: true, | |
| position: 'bottom', | |
| labels: { | |
| color: '#1A2B3C', | |
| padding: 20, | |
| font: { | |
| family: 'Inter' | |
| } | |
| } | |
| }, | |
| title: { | |
| display: true, | |
| text: 'Risk Factors Analysis', | |
| color: '#1A2B3C', | |
| font: { | |
| size: 16, | |
| weight: 'bold', | |
| family: 'Inter' | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| // Auto-update flight status | |
| function updateFlightStatus() { | |
| const onTimeElement = document.querySelector('.status-on-time').parentElement.querySelector('span'); | |
| const delayedElement = document.querySelector('.status-delayed').parentElement.querySelector('span'); | |
| // Simulate real-time updates | |
| const onTimeRate = 75 + Math.random() * 10; | |
| const delayedRate = 100 - onTimeRate; | |
| onTimeElement.textContent = `On Time: ${onTimeRate.toFixed(1)}%`; | |
| delayedElement.textContent = `Delayed: ${delayedRate.toFixed(1)}%`; | |
| } | |
| // Add parallax effect | |
| window.addEventListener('scroll', function() { | |
| const scrolled = window.pageYOffset; | |
| const parallaxElements = document.querySelectorAll('.parallax-element'); | |
| parallaxElements.forEach(element => { | |
| const speed = element.dataset.speed || 0.5; | |
| element.style.transform = `translateY(${scrolled * speed}px)`; | |
| }); | |
| // Add parallax to hero section | |
| const heroSection = document.querySelector('.hero-section'); | |
| if (heroSection) { | |
| heroSection.style.transform = `translateY(${scrolled * 0.3}px)`; | |
| } | |
| }); | |
| // Test function to debug prediction API | |
| async function testPrediction() { | |
| try { | |
| console.log('Testing prediction API...'); | |
| // Test the test endpoint first | |
| const testResponse = await fetch('/api/test'); | |
| const testData = await testResponse.json(); | |
| console.log('Test endpoint result:', testData); | |
| // Test the prediction endpoint with sample data | |
| const formData = new FormData(); | |
| formData.append('airline', 'Air India'); | |
| formData.append('origin', 'Indira Gandhi International Airport (DEL)'); | |
| formData.append('destination', 'Chatrapati Shivaji International Airport (BOM)'); | |
| formData.append('departure_hour', '10'); | |
| formData.append('flight_date', '2024-12-15'); | |
| console.log('Sending prediction request...'); | |
| const response = await fetch('/api/predict', { | |
| method: 'POST', | |
| body: formData | |
| }); | |
| console.log('Response status:', response.status); | |
| if (!response.ok) { | |
| const errorText = await response.text(); | |
| console.error('Server error:', errorText); | |
| alert('Server error: ' + response.status + ' - ' + errorText); | |
| return; | |
| } | |
| const result = await response.json(); | |
| console.log('Prediction result:', result); | |
| if (result.error) { | |
| alert('Prediction error: ' + result.error); | |
| } else { | |
| alert('Prediction successful! Status: ' + result.status + ', Probability: ' + result.probability + '%'); | |
| } | |
| } catch (error) { | |
| console.error('Test error:', error); | |
| alert('Test error: ' + error.message); | |
| } | |
| } | |
| // Add smooth scroll behavior | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Load form data | |
| loadFormData(); | |
| // Load insights | |
| loadInsights(); | |
| // Update flight status every 5 seconds | |
| setInterval(updateFlightStatus, 5000); | |
| // Add smooth scroll behavior | |
| document.documentElement.style.scrollBehavior = 'smooth'; | |
| // Add parallax class to elements | |
| const heroTitle = document.querySelector('.hero-title'); | |
| const heroSubtitle = document.querySelector('.hero-subtitle'); | |
| const heroButtons = document.querySelector('.hero-buttons'); | |
| if (heroTitle) heroTitle.classList.add('parallax-element'); | |
| if (heroSubtitle) heroSubtitle.classList.add('parallax-element'); | |
| if (heroButtons) heroButtons.classList.add('parallax-element'); | |
| // Set different speeds for different elements | |
| if (heroTitle) heroTitle.dataset.speed = '0.2'; | |
| if (heroSubtitle) heroSubtitle.dataset.speed = '0.3'; | |
| if (heroButtons) heroButtons.dataset.speed = '0.4'; | |
| }); | |
| // Add intersection observer for animations | |
| const observerOptions = { | |
| threshold: 0.1, | |
| rootMargin: '0px 0px -50px 0px' | |
| }; | |
| const observer = new IntersectionObserver((entries) => { | |
| entries.forEach(entry => { | |
| if (entry.isIntersecting) { | |
| entry.target.style.opacity = '1'; | |
| entry.target.style.transform = 'translateY(0)'; | |
| } | |
| }); | |
| }, observerOptions); | |
| // Observe all cards for animation | |
| document.addEventListener('DOMContentLoaded', function() { | |
| const cards = document.querySelectorAll('.feature-card, .dashboard-card, .impact-card'); | |
| cards.forEach(card => { | |
| card.style.opacity = '0'; | |
| card.style.transform = 'translateY(20px)'; | |
| card.style.transition = 'opacity 0.6s ease, transform 0.6s ease'; | |
| observer.observe(card); | |
| }); | |
| }); | |