feat: enhance dashboard with new metrics, project and scenario utilities, and comprehensive tests
This commit is contained in:
@@ -1,13 +1,14 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Iterable
|
||||
from datetime import datetime
|
||||
from typing import Sequence
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy import select, func
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy.orm import Session, joinedload, selectinload
|
||||
|
||||
from models import FinancialInput, Project, Scenario, SimulationParameter
|
||||
from models import FinancialInput, Project, Scenario, ScenarioStatus, SimulationParameter
|
||||
from services.exceptions import EntityConflictError, EntityNotFoundError
|
||||
|
||||
|
||||
@@ -23,6 +24,18 @@ class ProjectRepository:
|
||||
stmt = stmt.options(selectinload(Project.scenarios))
|
||||
return self.session.execute(stmt).scalars().all()
|
||||
|
||||
def count(self) -> int:
|
||||
stmt = select(func.count(Project.id))
|
||||
return self.session.execute(stmt).scalar_one()
|
||||
|
||||
def recent(self, limit: int = 5) -> Sequence[Project]:
|
||||
stmt = (
|
||||
select(Project)
|
||||
.order_by(Project.updated_at.desc())
|
||||
.limit(limit)
|
||||
)
|
||||
return self.session.execute(stmt).scalars().all()
|
||||
|
||||
def get(self, project_id: int, *, with_children: bool = False) -> Project:
|
||||
stmt = select(Project).where(Project.id == project_id)
|
||||
if with_children:
|
||||
@@ -60,6 +73,39 @@ class ScenarioRepository:
|
||||
)
|
||||
return self.session.execute(stmt).scalars().all()
|
||||
|
||||
def count(self) -> int:
|
||||
stmt = select(func.count(Scenario.id))
|
||||
return self.session.execute(stmt).scalar_one()
|
||||
|
||||
def count_by_status(self, status: ScenarioStatus) -> int:
|
||||
stmt = select(func.count(Scenario.id)).where(Scenario.status == status)
|
||||
return self.session.execute(stmt).scalar_one()
|
||||
|
||||
def recent(self, limit: int = 5, *, with_project: bool = False) -> Sequence[Scenario]:
|
||||
stmt = select(Scenario).order_by(
|
||||
Scenario.updated_at.desc()).limit(limit)
|
||||
if with_project:
|
||||
stmt = stmt.options(joinedload(Scenario.project))
|
||||
return self.session.execute(stmt).scalars().all()
|
||||
|
||||
def list_by_status(
|
||||
self,
|
||||
status: ScenarioStatus,
|
||||
*,
|
||||
limit: int | None = None,
|
||||
with_project: bool = False,
|
||||
) -> Sequence[Scenario]:
|
||||
stmt = (
|
||||
select(Scenario)
|
||||
.where(Scenario.status == status)
|
||||
.order_by(Scenario.updated_at.desc())
|
||||
)
|
||||
if with_project:
|
||||
stmt = stmt.options(joinedload(Scenario.project))
|
||||
if limit is not None:
|
||||
stmt = stmt.limit(limit)
|
||||
return self.session.execute(stmt).scalars().all()
|
||||
|
||||
def get(self, scenario_id: int, *, with_children: bool = False) -> Scenario:
|
||||
stmt = select(Scenario).where(Scenario.id == scenario_id)
|
||||
if with_children:
|
||||
@@ -119,6 +165,14 @@ class FinancialInputRepository:
|
||||
raise EntityNotFoundError(f"Financial input {input_id} not found")
|
||||
self.session.delete(entity)
|
||||
|
||||
def latest_created_at(self) -> datetime | None:
|
||||
stmt = (
|
||||
select(FinancialInput.created_at)
|
||||
.order_by(FinancialInput.created_at.desc())
|
||||
.limit(1)
|
||||
)
|
||||
return self.session.execute(stmt).scalar_one_or_none()
|
||||
|
||||
|
||||
class SimulationParameterRepository:
|
||||
"""Persistence operations for SimulationParameter entities."""
|
||||
|
||||
Reference in New Issue
Block a user