238 lines
11 KiB
HTML
Executable File
238 lines
11 KiB
HTML
Executable File
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Analysis Results</title>
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<script src="https://cdn.jsdelivr.net/npm/plotly@2.27.0/dist/plotly.min.js"></script>
|
|
<style>
|
|
body {
|
|
background-color: #f8f9fa;
|
|
}
|
|
.container {
|
|
max-width: 1200px;
|
|
margin-top: 20px;
|
|
}
|
|
.collapsible-section {
|
|
margin-bottom: 15px;
|
|
}
|
|
.collapsible-section .btn-toggle {
|
|
background-color: #007bff;
|
|
color: white;
|
|
width: 100%;
|
|
text-align: left;
|
|
padding: 10px;
|
|
border-radius: 5px;
|
|
}
|
|
.collapsible-section .btn-toggle:hover {
|
|
background-color: #0056b3;
|
|
}
|
|
.collapsible-section .collapse {
|
|
margin-top: 10px;
|
|
}
|
|
.plotly-container {
|
|
width: 100%;
|
|
max-width: none;
|
|
margin: 0;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<!-- Warning Message -->
|
|
<div class="alert alert-warning alert-dismissible fade show" role="alert">
|
|
<strong>Warning:</strong> This app does not save files or outputs. All work will be lost when the page is closed.
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
</div>
|
|
|
|
<h1 class="text-center mb-4">Analysis Results</h1>
|
|
|
|
<!-- Summary Statistics -->
|
|
<div class="collapsible-section">
|
|
<button class="btn btn-toggle" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-summary" aria-expanded="true" aria-controls="collapse-summary">
|
|
Summary Statistics
|
|
</button>
|
|
<div class="collapse show" id="collapse-summary">
|
|
<div class="card card-body">
|
|
<h5>Summary Statistics</h5>
|
|
<table class="table">
|
|
{% for key, value in summary.items() %}
|
|
<tr>
|
|
<td>{{ key }}</td>
|
|
<td>{{ value | round(2) }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</table>
|
|
<a href="{{ url_for('download_file', filename=filename) }}" class="btn btn-secondary">Download Processed Data</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Decomposition -->
|
|
{% if do_decomposition and plot_html %}
|
|
<div class="collapsible-section">
|
|
<button class="btn btn-toggle" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-decomposition" aria-expanded="false" aria-controls="collapse-decomposition">
|
|
Decomposition
|
|
</button>
|
|
<div class="collapse" id="collapse-decomposition">
|
|
<div class="card card-body">
|
|
<h5>Decomposition</h5>
|
|
<div class="plotly-container" style="width: 1200px; height: 800px;">{{ plot_html | safe }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- ACF/PACF Plots -->
|
|
{% if do_acf_pacf and acf_pacf_html %}
|
|
<div class="collapsible-section">
|
|
<button class="btn btn-toggle" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-acf-pacf" aria-expanded="false" aria-controls="collapse-acf-pacf">
|
|
ACF/PACF Plots
|
|
</button>
|
|
<div class="collapse" id="collapse-acf-pacf">
|
|
<div class="card card-body">
|
|
<h5>ACF/PACF Plots</h5>
|
|
<div class="plotly-container" style="width: 1200px; height: 800px;">{{ acf_pacf_html | safe }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Forecasting -->
|
|
{% if do_forecasting and forecast_html %}
|
|
<div class="collapsible-section">
|
|
<button class="btn btn-toggle" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-forecasting" aria-expanded="{{ 'true' if scroll_to_forecast else 'false' }}" aria-controls="collapse-forecasting">
|
|
Forecasting
|
|
</button>
|
|
<div class="collapse {{ 'show' if scroll_to_forecast else '' }}" id="collapse-forecasting">
|
|
<div class="card card-body">
|
|
<h5>Forecast Plot</h5>
|
|
<p><strong>Model:</strong> {{ model_type }}</p>
|
|
{% if model_params %}
|
|
<p><strong>Model Parameters:</strong> {{ model_params }}</p>
|
|
{% endif %}
|
|
{% if metrics %}
|
|
<p><strong>Test Set Metrics:</strong></p>
|
|
<ul>
|
|
<li>MAE: {{ metrics.MAE | round(2) }}</li>
|
|
<li>MSE: {{ metrics.MSE | round(2) }}</li>
|
|
<li>RMSE: {{ metrics.RMSE | round(2) }}</li>
|
|
</ul>
|
|
{% endif %}
|
|
<div class="plotly-container" style="width: 1200px; height: 400px;">{{ forecast_html | safe }}</div>
|
|
|
|
{% if forecast_history %}
|
|
<h5 class="mt-4">Forecast History</h5>
|
|
<form method="post" action="{{ url_for('compare_forecasts') }}">
|
|
<table class="table">
|
|
<thead>
|
|
<tr>
|
|
<th>Select</th>
|
|
<th>Run</th>
|
|
<th>Train Percent (%)</th>
|
|
<th>Test Percent (%)</th>
|
|
<th>Forecast Periods</th>
|
|
<th>MAE</th>
|
|
<th>MSE</th>
|
|
<th>RMSE</th>
|
|
<th>Model</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for entry in forecast_history %}
|
|
<tr>
|
|
<td><input type="checkbox" name="selected_forecasts" value="{{ loop.index0 }}" {{ 'checked' if loop.index0 in selected_indices else '' }}></td>
|
|
<td>{{ loop.index }}</td>
|
|
<td>{{ entry.train_percent | round(2) }}</td>
|
|
<td>{{ entry.test_percent | round(2) }}</td>
|
|
<td>{{ entry.forecast_periods }}</td>
|
|
<td>{{ entry.mae | round(2) if entry.mae else '-' }}</td>
|
|
<td>{{ entry.mse | round(2) if entry.mse else '-' }}</td>
|
|
<td>{{ entry.rmse | round(2) if entry.rmse else '-' }}</td>
|
|
<td>{{ entry.model_type }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
<button type="submit" class="btn btn-primary">Compare Selected Forecasts</button>
|
|
</form>
|
|
<a href="{{ url_for('download_forecast_history') }}" class="btn btn-secondary mt-2">Download Forecast History</a>
|
|
{% endif %}
|
|
|
|
<h5 class="mt-4">Re-forecast</h5>
|
|
<form method="post" action="{{ url_for('reforecast') }}">
|
|
<div class="mb-3">
|
|
<label for="train_percent" class="form-label">Train Percentage</label>
|
|
<input type="number" class="form-control" id="train_percent" name="train_percent" value="{{ train_percent }}" min="1" max="99" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="test_percent" class="form-label">Test Percentage</label>
|
|
<input type="number" class="form-control" id="test_percent" name="test_percent" value="{{ test_percent }}" min="1" max="99" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="forecast_periods" class="form-label">Forecast Periods</label>
|
|
<input type="number" class="form-control" id="forecast_periods" name="forecast_periods" value="{{ forecast_periods }}" min="1" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="model_type" class="form-label">Forecast Model</label>
|
|
<select class="form-control" id="model_type" name="model_type">
|
|
<option value="ARIMA" {{ 'selected' if model_type == 'ARIMA' else '' }}>ARIMA</option>
|
|
<option value="Exponential Smoothing" {{ 'selected' if model_type == 'Exponential Smoothing' else '' }}>Exponential Smoothing</option>
|
|
<option value="Prophet" {{ 'selected' if model_type == 'Prophet' else '' }}>Prophet</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-check mb-3">
|
|
<input type="checkbox" class="form-check-input" id="add_to_existing" name="add_to_existing">
|
|
<label class="form-check-label" for="add_to_existing">Add to Existing Plots</label>
|
|
</div>
|
|
<button type="submit" class="btn btn-primary">Run Re-forecast</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script>
|
|
// Sync Train and Test Percentage inputs
|
|
const trainInput = document.getElementById('train_percent');
|
|
const testInput = document.getElementById('test_percent');
|
|
|
|
function syncPercentages(source, target) {
|
|
source.addEventListener('input', () => {
|
|
const value = parseFloat(source.value);
|
|
if (!isNaN(value) && value >= 1 && value <= 99) {
|
|
target.value = (100 - value).toFixed(0);
|
|
} else {
|
|
target.value = '';
|
|
}
|
|
});
|
|
}
|
|
|
|
syncPercentages(trainInput, testInput);
|
|
syncPercentages(testInput, trainInput);
|
|
|
|
// Scroll to and expand forecasting section if scroll_to_forecast is true
|
|
{% if scroll_to_forecast %}
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const forecastingSection = document.querySelector('#collapse-forecasting');
|
|
if (forecastingSection) {
|
|
forecastingSection.classList.add('show');
|
|
forecastingSection.scrollIntoView({ behavior: 'smooth' });
|
|
}
|
|
});
|
|
{% endif %}
|
|
|
|
// Resize Plotly plots on section expand
|
|
document.querySelectorAll('.collapse').forEach(collapse => {
|
|
collapse.addEventListener('shown.bs.collapse', () => {
|
|
if (typeof Plotly !== 'undefined') {
|
|
Plotly.Plots.resize(collapse.querySelector('.plotly-container'));
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
</body>
|
|
</html> |