|
|
document.addEventListener('DOMContentLoaded', () => { |
|
|
const navLinks = document.querySelectorAll('header nav a'); |
|
|
navLinks.forEach(link => { |
|
|
link.addEventListener('click', function(event) { |
|
|
event.stopImmediatePropagation(); |
|
|
window.location.href = this.href; |
|
|
}, true); |
|
|
}); |
|
|
|
|
|
console.log('Landing page loaded.'); |
|
|
|
|
|
|
|
|
const animatedElements = document.querySelectorAll('.animate-on-scroll'); |
|
|
const observer = new IntersectionObserver(entries => { |
|
|
entries.forEach(entry => { |
|
|
if (entry.isIntersecting) { |
|
|
entry.target.classList.add('animate-fadeIn'); |
|
|
observer.unobserve(entry.target); |
|
|
} |
|
|
}); |
|
|
}, { threshold: 0.2 }); |
|
|
|
|
|
animatedElements.forEach(el => observer.observe(el)); |
|
|
|
|
|
|
|
|
const initialPlotDiv = document.getElementById('initialPlot'); |
|
|
if (initialPlotDiv) { |
|
|
const plotData = JSON.parse(initialPlotDiv.dataset.plot); |
|
|
Plotly.newPlot('plotDiv', plotData.data, plotData.layout); |
|
|
} |
|
|
|
|
|
|
|
|
if (!document.getElementById('toast-container')) { |
|
|
const toastContainer = document.createElement('div'); |
|
|
toastContainer.id = 'toast-container'; |
|
|
toastContainer.className = 'fixed top-4 right-4 z-50'; |
|
|
document.body.appendChild(toastContainer); |
|
|
} |
|
|
}); |
|
|
|
|
|
function showToast(message, type = 'success') { |
|
|
const toast = document.createElement('div'); |
|
|
toast.className = `p-4 mb-4 rounded shadow-lg ${type === 'success' ? 'bg-green-500' : 'bg-red-500'} text-white`; |
|
|
toast.textContent = message; |
|
|
|
|
|
const container = document.getElementById('toast-container'); |
|
|
container.appendChild(toast); |
|
|
|
|
|
setTimeout(() => { |
|
|
toast.remove(); |
|
|
}, 3000); |
|
|
} |
|
|
|
|
|
window.createPlot = function() { |
|
|
const column = document.getElementById('plotColumn').value; |
|
|
const plotType = document.getElementById('plotType').value; |
|
|
|
|
|
fetch('/forecast/sales/plot', { |
|
|
method: 'POST', |
|
|
headers: { |
|
|
'Content-Type': 'application/x-www-form-urlencoded', |
|
|
}, |
|
|
body: `column=${column}&plot_type=${plotType}` |
|
|
}) |
|
|
.then(response => response.json()) |
|
|
.then(data => { |
|
|
if (data.success) { |
|
|
const plotData = JSON.parse(data.plot); |
|
|
Plotly.newPlot('plotDiv', plotData.data, plotData.layout); |
|
|
showToast(`Generated ${plotType} for ${column}`); |
|
|
} else { |
|
|
showToast(data.error, 'error'); |
|
|
} |
|
|
}) |
|
|
.catch(error => { |
|
|
console.error('Error:', error); |
|
|
showToast(error.message, 'error'); |
|
|
}); |
|
|
} |
|
|
|
|
|
window.fixNulls = function(column) { |
|
|
const method = document.getElementById('method-' + column).value; |
|
|
fetch('/forecast/sales/fix_nulls', { |
|
|
method: 'POST', |
|
|
headers: {'Content-Type': 'application/x-www-form-urlencoded'}, |
|
|
body: `column=${column}&method=${method}` |
|
|
}) |
|
|
.then(response => response.json()) |
|
|
.then(data => { |
|
|
const msgDiv = document.getElementById('null-fix-message'); |
|
|
if (data.success) { |
|
|
msgDiv.textContent = data.message; |
|
|
msgDiv.classList.remove('text-red-700'); |
|
|
msgDiv.classList.add('text-green-700'); |
|
|
showToast(data.message); |
|
|
|
|
|
|
|
|
updateSummaryStats(data.summary_stats); |
|
|
|
|
|
|
|
|
updatePreviewTable(data.columns, data.preview_data); |
|
|
|
|
|
|
|
|
updateNullsUI(data.summary_stats.missing_values); |
|
|
|
|
|
} else { |
|
|
msgDiv.textContent = data.error; |
|
|
msgDiv.classList.remove('text-green-700'); |
|
|
msgDiv.classList.add('text-red-700'); |
|
|
showToast(data.error, 'error'); |
|
|
} |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
function updateSummaryStats(stats) { |
|
|
document.querySelectorAll('.summary-total-rows').forEach(el => el.textContent = stats.total_rows); |
|
|
document.querySelectorAll('.summary-total-cols').forEach(el => el.textContent = stats.total_columns); |
|
|
document.querySelectorAll('.summary-numeric-cols').forEach(el => el.textContent = stats.numeric_columns.join(', ')); |
|
|
document.querySelectorAll('.summary-categorical-cols').forEach(el => el.textContent = stats.categorical_columns.join(', ')); |
|
|
} |
|
|
|
|
|
|
|
|
function updatePreviewTable(columns, previewData) { |
|
|
const table = document.getElementById('preview-table'); |
|
|
if (!table) return; |
|
|
|
|
|
let thead = table.querySelector('thead'); |
|
|
let tbody = table.querySelector('tbody'); |
|
|
thead.innerHTML = '<tr>' + columns.map(col => `<th class="px-4 py-2 bg-gray-100">${col}</th>`).join('') + '</tr>'; |
|
|
|
|
|
tbody.innerHTML = previewData.map(row => |
|
|
'<tr>' + columns.map(col => `<td class="border px-4 py-2">${row[col]}</td>`).join('') + '</tr>' |
|
|
).join(''); |
|
|
} |
|
|
|
|
|
|
|
|
function updateNullsUI(missingValues) { |
|
|
const nullList = document.getElementById('null-list'); |
|
|
if (!nullList) return; |
|
|
let html = ''; |
|
|
let hasNulls = false; |
|
|
for (const [col, count] of Object.entries(missingValues)) { |
|
|
if (count > 0) { |
|
|
hasNulls = true; |
|
|
html += ` |
|
|
<li> |
|
|
<span>${col}: ${count} missing values</span> |
|
|
<select id="method-${col}" class="border rounded p-1 mx-2"> |
|
|
<option value="drop">Drop Rows</option> |
|
|
<option value="mean">Fill Mean</option> |
|
|
<option value="median">Fill Median</option> |
|
|
<option value="mode">Fill Mode</option> |
|
|
</select> |
|
|
<button class="btn-learn-more" onclick="fixNulls('${col}')">Fix</button> |
|
|
</li> |
|
|
`; |
|
|
} |
|
|
} |
|
|
nullList.innerHTML = html; |
|
|
|
|
|
if (!hasNulls) { |
|
|
nullList.innerHTML = '<li class="text-green-700 font-semibold">No missing values remaining!</li>'; |
|
|
} |
|
|
} |
|
|
window.runForecast = function() { |
|
|
const dateCol = document.getElementById('forecastDateCol').value; |
|
|
const targetCol = document.getElementById('forecastTargetCol').value; |
|
|
const model = document.getElementById('forecastModel').value; |
|
|
const horizon = document.getElementById('forecastHorizon').value; |
|
|
const metricsDiv = document.getElementById('forecast-metrics'); |
|
|
const plotDiv = document.getElementById('forecast-plot'); |
|
|
const errorDiv = document.getElementById('forecast-error'); |
|
|
metricsDiv.innerHTML = ''; |
|
|
plotDiv.innerHTML = ''; |
|
|
errorDiv.textContent = ''; |
|
|
|
|
|
fetch('/forecast/sales/run_forecast', { |
|
|
method: 'POST', |
|
|
headers: {'Content-Type': 'application/x-www-form-urlencoded'}, |
|
|
body: `date_col=${dateCol}&target_col=${targetCol}&model=${model}&horizon=${horizon}` |
|
|
}) |
|
|
.then(response => response.json()) |
|
|
.then(data => { |
|
|
if (data.success) { |
|
|
|
|
|
metricsDiv.innerHTML = ` |
|
|
<div class="mb-2"> |
|
|
<strong>Model:</strong> ${data.model} |
|
|
</div> |
|
|
<div class="mb-2"> |
|
|
<strong>MAPE:</strong> ${data.metrics.MAPE.toFixed(2)}% |
|
|
<strong class="ml-4">RMSE:</strong> ${data.metrics.RMSE.toFixed(2)} |
|
|
<strong class="ml-4">R²:</strong> ${data.metrics.R2.toFixed(3)} |
|
|
</div> |
|
|
`; |
|
|
|
|
|
const trace = { |
|
|
x: data.dates, |
|
|
y: data.forecast, |
|
|
mode: 'lines+markers', |
|
|
name: 'Forecast' |
|
|
}; |
|
|
const lower = data.conf_int.map(ci => ci[0]); |
|
|
const upper = data.conf_int.map(ci => ci[1]); |
|
|
const ciTrace = { |
|
|
x: [...data.dates, ...data.dates.slice().reverse()], |
|
|
y: [...upper, ...lower.reverse()], |
|
|
fill: 'toself', |
|
|
fillcolor: 'rgba(0,100,80,0.2)', |
|
|
line: {color: 'transparent'}, |
|
|
name: 'Confidence Interval', |
|
|
showlegend: true, |
|
|
type: 'scatter' |
|
|
}; |
|
|
const layout = { |
|
|
title: 'Forecasted Sales', |
|
|
xaxis: {title: 'Date'}, |
|
|
yaxis: {title: 'Forecast'}, |
|
|
showlegend: true |
|
|
}; |
|
|
Plotly.newPlot(plotDiv, [trace, ciTrace], layout); |
|
|
showToast('Forecast generated!'); |
|
|
} else { |
|
|
errorDiv.textContent = data.error; |
|
|
showToast(data.error, 'error'); |
|
|
} |
|
|
}) |
|
|
.catch(error => { |
|
|
errorDiv.textContent = error.message; |
|
|
showToast(error.message, 'error'); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function runPrediction() { |
|
|
|
|
|
const metricsDiv = document.getElementById('predict-metrics'); |
|
|
const errorDiv = document.getElementById('predict-error'); |
|
|
const singlePredictionSection = document.getElementById('single-prediction-section'); |
|
|
|
|
|
metricsDiv.innerHTML = '<p>Running prediction...</p>'; |
|
|
errorDiv.textContent = ''; |
|
|
singlePredictionSection.classList.add('hidden'); |
|
|
|
|
|
|
|
|
const targetCol = document.getElementById('predictTargetCol').value; |
|
|
const model = document.getElementById('predictModel').value; |
|
|
|
|
|
|
|
|
const formData = new FormData(); |
|
|
formData.append('target_col', targetCol); |
|
|
formData.append('model', model); |
|
|
|
|
|
|
|
|
fetch('/predict/machine_failure/run_prediction', { |
|
|
method: 'POST', |
|
|
body: formData |
|
|
}) |
|
|
.then(response => response.json()) |
|
|
.then(data => { |
|
|
if (data.success) { |
|
|
|
|
|
let metricsHtml = |
|
|
'<div class="grid grid-cols-2 md:grid-cols-4 gap-4">'; |
|
|
for (const [key, value] of Object.entries(data.metrics)) { |
|
|
metricsHtml += ` |
|
|
<div class="p-4 bg-blue-50 rounded"> |
|
|
<p class="font-semibold">${key}</p> |
|
|
<p class="text-xl">${value.toFixed(4)}</p> |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
metricsHtml += "</div>"; |
|
|
metricsDiv.innerHTML = metricsHtml; |
|
|
|
|
|
|
|
|
if (data.top_features && Array.isArray(data.top_features)) { |
|
|
const fiDiv = document.getElementById("feature-importance"); |
|
|
const fiList = document.getElementById("feature-importance-list"); |
|
|
fiDiv.classList.remove("hidden"); |
|
|
fiList.innerHTML = ` |
|
|
<table class="min-w-full table-auto"> |
|
|
<thead> |
|
|
<tr> |
|
|
<th class="px-4 py-2 bg-gray-100">Feature</th> |
|
|
<th class="px-4 py-2 bg-gray-100">Importance</th> |
|
|
</tr> |
|
|
</thead> |
|
|
<tbody> |
|
|
${data.top_features |
|
|
.map( |
|
|
(f) => |
|
|
`<tr> |
|
|
<td class="border px-4 py-2">${ |
|
|
f.feature |
|
|
}</td> |
|
|
<td class="border px-4 py-2">${f.importance.toFixed( |
|
|
4 |
|
|
)}</td> |
|
|
</tr>` |
|
|
) |
|
|
.join("")} |
|
|
</tbody> |
|
|
</table> |
|
|
`; |
|
|
} |
|
|
|
|
|
showToast("Model trained successfully"); |
|
|
|
|
|
|
|
|
fetchSinglePredictionForm(); |
|
|
} else { |
|
|
errorDiv.textContent = data.error || 'An error occurred'; |
|
|
showToast(data.error || 'An error occurred', 'error'); |
|
|
} |
|
|
}) |
|
|
.catch(error => { |
|
|
errorDiv.textContent = 'Error: ' + error.message; |
|
|
showToast('Error: ' + error.message, 'error'); |
|
|
}); |
|
|
} |
|
|
|
|
|
window.runPrediction = runPrediction; |
|
|
|
|
|
|
|
|
function fetchSinglePredictionForm() { |
|
|
fetch('/predict/machine_failure/get_form_data') |
|
|
.then(response => response.json()) |
|
|
.then(data => { |
|
|
if (data.success) { |
|
|
generatePredictionForm(data.form_fields); |
|
|
document.getElementById('single-prediction-section').classList.remove('hidden'); |
|
|
} else { |
|
|
showToast(data.error, 'error'); |
|
|
document.getElementById('single-prediction-error').textContent = data.error; |
|
|
} |
|
|
}) |
|
|
.catch(error => { |
|
|
console.error('Error fetching form data:', error); |
|
|
showToast('Error fetching form data: ' + error.message, 'error'); |
|
|
document.getElementById('single-prediction-error').textContent = 'Error fetching form data: ' + error.message; |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
function generatePredictionForm(formFields) { |
|
|
const formContainer = document.getElementById('single-prediction-form'); |
|
|
formContainer.innerHTML = ''; |
|
|
|
|
|
formFields.forEach(field => { |
|
|
const div = document.createElement('div'); |
|
|
div.className = 'mb-4'; |
|
|
|
|
|
const label = document.createElement('label'); |
|
|
label.className = 'block text-gray-700 text-sm font-bold mb-2'; |
|
|
label.textContent = field.name.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()); |
|
|
|
|
|
div.appendChild(label); |
|
|
|
|
|
if (field.type === 'select') { |
|
|
const select = document.createElement('select'); |
|
|
select.id = `input-${field.name}`; |
|
|
select.name = field.name; |
|
|
select.className = 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline'; |
|
|
|
|
|
field.options.forEach(option => { |
|
|
const optionElement = document.createElement('option'); |
|
|
optionElement.value = option; |
|
|
optionElement.textContent = option; |
|
|
|
|
|
if (field.default_value !== undefined && String(field.default_value) === String(option)) { |
|
|
optionElement.selected = true; |
|
|
} |
|
|
select.appendChild(optionElement); |
|
|
}); |
|
|
div.appendChild(select); |
|
|
} else if (field.type === 'number') { |
|
|
const input = document.createElement('input'); |
|
|
input.id = `input-${field.name}`; |
|
|
input.name = field.name; |
|
|
input.type = 'number'; |
|
|
input.className = 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline'; |
|
|
input.placeholder = `Enter ${field.name.replace(/_/g, ' ')}`; |
|
|
if (field.default_value !== undefined) { |
|
|
input.value = field.default_value; |
|
|
} |
|
|
div.appendChild(input); |
|
|
} else { |
|
|
const input = document.createElement('input'); |
|
|
input.id = `input-${field.name}`; |
|
|
input.name = field.name; |
|
|
input.type = 'text'; |
|
|
input.className = 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline'; |
|
|
input.placeholder = field.placeholder || `Enter ${field.name.replace(/_/g, ' ')}`; |
|
|
if (field.default_value !== undefined) { |
|
|
input.value = field.default_value; |
|
|
} |
|
|
div.appendChild(input); |
|
|
} |
|
|
formContainer.appendChild(div); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
window.predictSingleInstance = function() { |
|
|
const form = document.getElementById('single-prediction-form'); |
|
|
const formData = {}; |
|
|
const inputs = form.querySelectorAll('input, select'); |
|
|
|
|
|
inputs.forEach(input => { |
|
|
formData[input.name] = input.value; |
|
|
}); |
|
|
|
|
|
const resultDiv = document.getElementById('single-prediction-result'); |
|
|
const predictionOutput = document.getElementById('prediction-output'); |
|
|
const probabilityOutput = document.getElementById('probability-output'); |
|
|
const errorDiv = document.getElementById('single-prediction-error'); |
|
|
|
|
|
resultDiv.classList.add('hidden'); |
|
|
errorDiv.textContent = ''; |
|
|
predictionOutput.textContent = 'Predicting...'; |
|
|
probabilityOutput.innerHTML = ''; |
|
|
|
|
|
fetch('/predict/machine_failure/predict_single', { |
|
|
method: 'POST', |
|
|
headers: { |
|
|
'Content-Type': 'application/json', |
|
|
}, |
|
|
body: JSON.stringify(formData) |
|
|
}) |
|
|
.then(response => response.json()) |
|
|
.then(data => { |
|
|
if (data.success) { |
|
|
let displayPrediction = 'Unknown'; |
|
|
if (data.prediction === 0 || data.prediction === '0') { |
|
|
displayPrediction = 'No Failure'; |
|
|
} else if (data.prediction === 1 || data.prediction === '1') { |
|
|
displayPrediction = 'Failure'; |
|
|
} else { |
|
|
displayPrediction = data.prediction; |
|
|
} |
|
|
predictionOutput.textContent = displayPrediction; |
|
|
|
|
|
if (data.probability && Array.isArray(data.probability)) { |
|
|
if (data.probability.length === 2) { |
|
|
const probNoFailure = data.probability[0]; |
|
|
const probFailure = data.probability[1]; |
|
|
probabilityOutput.innerHTML = `Probability of No Failure: ${(probNoFailure * 100).toFixed(2)}%<br> |
|
|
Probability of Failure: ${(probFailure * 100).toFixed(2)}%`; |
|
|
} else { |
|
|
probabilityOutput.innerHTML = 'Probabilities: ' + data.probability.map((p, i) => `Class ${i}: ${(p * 100).toFixed(2)}%`).join(', '); |
|
|
} |
|
|
} else { |
|
|
probabilityOutput.innerHTML = 'Probability: N/A (not a classification model)'; |
|
|
} |
|
|
resultDiv.classList.remove('hidden'); |
|
|
showToast('Single prediction successful!'); |
|
|
} else { |
|
|
errorDiv.textContent = data.error || 'An error occurred during single prediction.'; |
|
|
showToast(data.error || 'An error occurred', 'error'); |
|
|
} |
|
|
}) |
|
|
.catch(error => { |
|
|
console.error('Error during single prediction:', error); |
|
|
errorDiv.textContent = 'Error: ' + error.message; |
|
|
showToast('Error: ' + error.message, 'error'); |
|
|
}); |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
window.runSupplyPrediction = function() { |
|
|
const metricsDiv = document.getElementById('predict-metrics-supply'); |
|
|
const errorDiv = document.getElementById('predict-error-supply'); |
|
|
const singlePredictionSection = document.getElementById('single-prediction-section-supply'); |
|
|
|
|
|
metricsDiv.innerHTML = '<p>Running prediction...</p>'; |
|
|
errorDiv.textContent = ''; |
|
|
singlePredictionSection.classList.add('hidden'); |
|
|
|
|
|
|
|
|
const targetCol = 'failure_flag'; |
|
|
const model = document.getElementById('predictModelSupply').value; |
|
|
|
|
|
const formData = new FormData(); |
|
|
formData.append('target_col', targetCol); |
|
|
formData.append('model', model); |
|
|
|
|
|
fetch('/predict/supply_failure/run_prediction', { |
|
|
method: 'POST', |
|
|
body: formData |
|
|
}) |
|
|
.then(response => response.json()) |
|
|
.then(data => { |
|
|
if (data.success) { |
|
|
let metricsHtml = |
|
|
'<div class="grid grid-cols-2 md:grid-cols-4 gap-4">'; |
|
|
for (const [key, value] of Object.entries(data.metrics)) { |
|
|
metricsHtml += ` |
|
|
<div class="p-4 bg-blue-50 rounded"> |
|
|
<p class="font-semibold">${key}</p> |
|
|
<p class="text-xl">${value.toFixed(4)}</p> |
|
|
</div> |
|
|
`; |
|
|
} |
|
|
metricsHtml += "</div>"; |
|
|
metricsDiv.innerHTML = metricsHtml; |
|
|
|
|
|
|
|
|
if (data.top_features && Array.isArray(data.top_features)) { |
|
|
const fiDiv = document.getElementById("feature-importance"); |
|
|
const fiList = document.getElementById("feature-importance-list"); |
|
|
fiDiv.classList.remove("hidden"); |
|
|
fiList.innerHTML = ` |
|
|
<table class="min-w-full table-auto"> |
|
|
<thead> |
|
|
<tr> |
|
|
<th class="px-4 py-2 bg-gray-100">Feature</th> |
|
|
<th class="px-4 py-2 bg-gray-100">Importance</th> |
|
|
</tr> |
|
|
</thead> |
|
|
<tbody> |
|
|
${data.top_features |
|
|
.map( |
|
|
(f) => |
|
|
`<tr> |
|
|
<td class="border px-4 py-2">${ |
|
|
f.feature |
|
|
}</td> |
|
|
<td class="border px-4 py-2">${f.importance.toFixed( |
|
|
4 |
|
|
)}</td> |
|
|
</tr>` |
|
|
) |
|
|
.join("")} |
|
|
</tbody> |
|
|
</table> |
|
|
`; |
|
|
} |
|
|
|
|
|
showToast("Supply Model trained successfully"); |
|
|
fetchSupplySinglePredictionForm(); |
|
|
} else { |
|
|
errorDiv.textContent = data.error || 'An error occurred'; |
|
|
showToast(data.error || 'An error occurred', 'error'); |
|
|
} |
|
|
}) |
|
|
.catch(error => { |
|
|
errorDiv.textContent = 'Error: ' + error.message; |
|
|
showToast('Error: ' + error.message, 'error'); |
|
|
}); |
|
|
} |
|
|
|
|
|
function fetchSupplySinglePredictionForm() { |
|
|
fetch('/predict/supply_failure/get_form_data') |
|
|
.then(response => response.json()) |
|
|
.then(data => { |
|
|
if (data.success) { |
|
|
generateSupplyPredictionForm(data.form_fields); |
|
|
document.getElementById('single-prediction-section-supply').classList.remove('hidden'); |
|
|
} else { |
|
|
showToast(data.error, 'error'); |
|
|
document.getElementById('single-prediction-error-supply').textContent = data.error; |
|
|
} |
|
|
}) |
|
|
.catch(error => { |
|
|
console.error('Error fetching supply form data:', error); |
|
|
showToast('Error fetching supply form data: ' + error.message, 'error'); |
|
|
document.getElementById('single-prediction-error-supply').textContent = 'Error fetching supply form data: ' + error.message; |
|
|
}); |
|
|
} |
|
|
|
|
|
function generateSupplyPredictionForm(formFields) { |
|
|
const formContainer = document.getElementById('single-prediction-form-supply'); |
|
|
formContainer.innerHTML = ''; |
|
|
|
|
|
formFields.forEach(field => { |
|
|
const div = document.createElement('div'); |
|
|
div.className = 'mb-4'; |
|
|
|
|
|
const label = document.createElement('label'); |
|
|
label.className = 'block text-gray-700 text-sm font-bold mb-2'; |
|
|
label.textContent = field.name.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()); |
|
|
|
|
|
div.appendChild(label); |
|
|
|
|
|
if (field.type === 'select') { |
|
|
const select = document.createElement('select'); |
|
|
select.id = `input-supply-${field.name}`; |
|
|
select.name = field.name; |
|
|
select.className = 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline'; |
|
|
|
|
|
field.options.forEach(option => { |
|
|
const optionElement = document.createElement('option'); |
|
|
optionElement.value = option; |
|
|
optionElement.textContent = option; |
|
|
if (field.default_value !== undefined && String(field.default_value) === String(option)) { |
|
|
optionElement.selected = true; |
|
|
} |
|
|
select.appendChild(optionElement); |
|
|
}); |
|
|
div.appendChild(select); |
|
|
} else if (field.type === 'number') { |
|
|
const input = document.createElement('input'); |
|
|
input.id = `input-supply-${field.name}`; |
|
|
input.name = field.name; |
|
|
input.type = 'number'; |
|
|
input.className = 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline'; |
|
|
input.placeholder = `Enter ${field.name.replace(/_/g, ' ')}`; |
|
|
if (field.default_value !== undefined) { |
|
|
input.value = field.default_value; |
|
|
} |
|
|
div.appendChild(input); |
|
|
} else { |
|
|
const input = document.createElement('input'); |
|
|
input.id = `input-supply-${field.name}`; |
|
|
input.name = field.name; |
|
|
input.type = 'text'; |
|
|
input.className = 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline'; |
|
|
input.placeholder = field.placeholder || `Enter ${field.name.replace(/_/g, ' ')}`; |
|
|
if (field.default_value !== undefined) { |
|
|
input.value = field.default_value; |
|
|
} |
|
|
div.appendChild(input); |
|
|
} |
|
|
formContainer.appendChild(div); |
|
|
}); |
|
|
} |
|
|
|
|
|
window.predictSupplySingleInstance = function() { |
|
|
const form = document.getElementById('single-prediction-form-supply'); |
|
|
const formData = {}; |
|
|
const inputs = form.querySelectorAll('input, select'); |
|
|
|
|
|
inputs.forEach(input => { |
|
|
formData[input.name] = input.value; |
|
|
}); |
|
|
|
|
|
const resultDiv = document.getElementById('single-prediction-result-supply'); |
|
|
const predictionOutput = document.getElementById('prediction-output-supply'); |
|
|
const probabilityOutput = document.getElementById('probability-output-supply'); |
|
|
const errorDiv = document.getElementById('single-prediction-error-supply'); |
|
|
|
|
|
resultDiv.classList.add('hidden'); |
|
|
errorDiv.textContent = ''; |
|
|
predictionOutput.textContent = 'Predicting...'; |
|
|
probabilityOutput.innerHTML = ''; |
|
|
|
|
|
fetch('/predict/supply_failure/predict_single', { |
|
|
method: 'POST', |
|
|
headers: { |
|
|
'Content-Type': 'application/json', |
|
|
}, |
|
|
body: JSON.stringify(formData) |
|
|
}) |
|
|
.then(response => response.json()) |
|
|
.then(data => { |
|
|
if (data.success) { |
|
|
|
|
|
let displayPrediction = 'Unknown'; |
|
|
if (data.prediction === "Delivery Successful") { |
|
|
displayPrediction = 'Delivery Successful'; |
|
|
} else if (data.prediction === "Delivery Failed") { |
|
|
displayPrediction = 'Delivery Failed'; |
|
|
} else { |
|
|
displayPrediction = data.prediction; |
|
|
} |
|
|
predictionOutput.textContent = displayPrediction; |
|
|
|
|
|
if (data.probability && Array.isArray(data.probability)) { |
|
|
if (data.probability.length === 2) { |
|
|
|
|
|
const probSuccessful = data.probability[0]; |
|
|
const probFailed = data.probability[1]; |
|
|
probabilityOutput.innerHTML = `Probability of Delivery Successful: ${(probSuccessful * 100).toFixed(2)}%<br> |
|
|
Probability of Delivery Failed: ${(probFailed * 100).toFixed(2)}%`; |
|
|
} else { |
|
|
probabilityOutput.innerHTML = 'Probabilities: ' + data.probability.map((p, i) => `Class ${i}: ${(p * 100).toFixed(2)}%`).join(', '); |
|
|
} |
|
|
} else { |
|
|
probabilityOutput.innerHTML = 'Probability: N/A (not a classification model)'; |
|
|
} |
|
|
resultDiv.classList.remove('hidden'); |
|
|
showToast('Single prediction successful!'); |
|
|
} else { |
|
|
errorDiv.textContent = data.error || 'An error occurred during single prediction.'; |
|
|
showToast(data.error || 'An error occurred', 'error'); |
|
|
} |
|
|
}) |
|
|
.catch(error => { |
|
|
console.error('Error during single prediction:', error); |
|
|
errorDiv.textContent = 'Error: ' + error.message; |
|
|
showToast('Error: ' + error.message, 'error'); |
|
|
}); |
|
|
}; |