Files
calminer/routes/dashboard.py

96 lines
3.0 KiB
Python

from __future__ import annotations
from datetime import datetime, timedelta
from types import SimpleNamespace
from fastapi import APIRouter, Depends, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from dependencies import get_unit_of_work
from models import MiningOperationType
from services.unit_of_work import UnitOfWork
router = APIRouter(tags=["Dashboard"])
templates = Jinja2Templates(directory="templates")
def _load_metrics(_: UnitOfWork) -> dict[str, object]:
today = datetime.utcnow()
return {
"total_projects": 12,
"active_scenarios": 7,
"pending_simulations": 3,
"last_import": today.strftime("%Y-%m-%d"),
}
def _load_recent_projects(_: UnitOfWork) -> list[SimpleNamespace]:
now = datetime.utcnow()
return [
SimpleNamespace(
id=1,
name="Copper Ridge Expansion",
operation_type=MiningOperationType.OPEN_PIT,
updated_at=now - timedelta(days=2),
),
SimpleNamespace(
id=2,
name="Lithium Basin North",
operation_type=MiningOperationType.UNDERGROUND,
updated_at=now - timedelta(days=5),
),
SimpleNamespace(
id=3,
name="Nickel Underground Phase II",
operation_type=MiningOperationType.IN_SITU_LEACH,
updated_at=now - timedelta(days=9),
),
]
def _load_simulation_updates(_: UnitOfWork) -> list[SimpleNamespace]:
now = datetime.utcnow()
return [
SimpleNamespace(
title="Monte Carlo Batch #21 completed",
description="1,000 runs processed for Lithium Basin North.",
timestamp=now - timedelta(hours=4),
),
SimpleNamespace(
title="Scenario validation queued",
description="Copper Ridge Expansion pending validation on new cost inputs.",
timestamp=now - timedelta(days=1, hours=3),
),
]
def _load_scenario_alerts(_: UnitOfWork) -> list[SimpleNamespace]:
return [
SimpleNamespace(
title="Variance exceeds threshold",
message="Nickel Underground Phase II deviates 18% from baseline forecast.",
link="/projects/3/view",
),
SimpleNamespace(
title="Simulation backlog",
message="Lithium Basin North has 2 pending simulation batches.",
link="/projects/2/view",
),
]
@router.get("/", response_class=HTMLResponse, include_in_schema=False, name="dashboard.home")
def dashboard_home(
request: Request,
uow: UnitOfWork = Depends(get_unit_of_work),
) -> HTMLResponse:
context = {
"request": request,
"metrics": _load_metrics(uow),
"recent_projects": _load_recent_projects(uow),
"simulation_updates": _load_simulation_updates(uow),
"scenario_alerts": _load_scenario_alerts(uow),
}
return templates.TemplateResponse("dashboard.html", context)