feat: Enhance dashboard metrics and summary statistics

- Added new summary fields: variance, 5th percentile, 95th percentile, VaR (95%), and expected shortfall (95%) to the dashboard.
- Updated the display logic for summary metrics to handle non-finite values gracefully.
- Modified the chart rendering to include additional percentile points and tail risk metrics in tooltips.

test: Introduce unit tests for consumption, costs, and other modules

- Created a comprehensive test suite for consumption, costs, equipment, maintenance, production, reporting, and simulation modules.
- Implemented fixtures for database setup and teardown using an in-memory SQLite database for isolated testing.
- Added tests for creating, listing, and validating various entities, ensuring proper error handling and response validation.

refactor: Consolidate parameter tests and remove deprecated files

- Merged parameter-related tests into a new test file for better organization and clarity.
- Removed the old parameter test file that was no longer in use.
- Improved test coverage for parameter creation, listing, and validation scenarios.

fix: Ensure proper validation and error handling in API endpoints

- Added validation to reject negative amounts in consumption and production records.
- Implemented checks to prevent duplicate scenario creation and ensure proper error messages are returned.
- Enhanced reporting endpoint tests to validate input formats and expected outputs.
This commit is contained in:
2025-10-20 22:06:39 +02:00
parent 606cb64ff1
commit 434be86b76
28 changed files with 945 additions and 401 deletions

View File

@@ -87,8 +87,16 @@
{ key: "min", label: "Min" },
{ key: "max", label: "Max" },
{ key: "std_dev", label: "Std Dev" },
{ key: "variance", label: "Variance" },
{ key: "percentile_5", label: "5th Percentile" },
{ key: "percentile_10", label: "10th Percentile" },
{ key: "percentile_90", label: "90th Percentile" },
{ key: "percentile_95", label: "95th Percentile" },
{ key: "value_at_risk_95", label: "VaR (95%)" },
{
key: "expected_shortfall_95",
label: "Expected Shortfall (95%)",
},
];
async function fetchSummary(results) {
@@ -123,12 +131,16 @@
const grid = document.getElementById("summary-grid");
grid.innerHTML = "";
SUMMARY_FIELDS.forEach(({ key, label }) => {
const value = summary[key] ?? 0;
const rawValue = summary[key];
const numericValue = Number(rawValue);
const display = Number.isFinite(numericValue)
? numericValue.toFixed(2)
: "—";
const metric = document.createElement("div");
metric.className = "metric";
metric.innerHTML = `
<div class="metric-label">${label}</div>
<div class="metric-value">${value.toFixed(2)}</div>
<div class="metric-value">${display}</div>
`;
grid.appendChild(metric);
});
@@ -138,14 +150,34 @@
function renderChart(summary) {
const ctx = document.getElementById("summary-chart").getContext("2d");
const dataPoints = [
summary.min,
summary.percentile_10,
summary.median,
summary.mean,
summary.percentile_90,
summary.max,
].map((value) => Number(value ?? 0));
const percentilePoints = [
{ label: "Min", value: summary.min },
{ label: "P5", value: summary.percentile_5 },
{ label: "P10", value: summary.percentile_10 },
{ label: "Median", value: summary.median },
{ label: "Mean", value: summary.mean },
{ label: "P90", value: summary.percentile_90 },
{ label: "P95", value: summary.percentile_95 },
{ label: "Max", value: summary.max },
];
const labels = percentilePoints.map((point) => point.label);
const dataPoints = percentilePoints.map((point) =>
Number(point.value ?? 0)
);
const tailRiskLines = [
{ label: "VaR (95%)", value: summary.value_at_risk_95 },
{ label: "ES (95%)", value: summary.expected_shortfall_95 },
]
.map(({ label, value }) => {
const numeric = Number(value);
if (!Number.isFinite(numeric)) {
return null;
}
return `${label}: ${numeric.toFixed(2)}`;
})
.filter((line) => line !== null);
if (chartInstance) {
chartInstance.destroy();
@@ -154,7 +186,7 @@
chartInstance = new Chart(ctx, {
type: "line",
data: {
labels: ["Min", "P10", "Median", "Mean", "P90", "Max"],
labels,
datasets: [
{
label: "Result Summary",
@@ -169,6 +201,11 @@
options: {
plugins: {
legend: { display: false },
tooltip: {
callbacks: {
afterBody: () => tailRiskLines,
},
},
},
scales: {
y: {