feat: add export filters for projects and scenarios with filtering capabilities

This commit is contained in:
2025-11-10 15:36:06 +01:00
parent b1a0153a8d
commit 1a7581cda0
4 changed files with 533 additions and 2 deletions

View File

@@ -11,6 +11,7 @@ from sqlalchemy.orm import Session, joinedload, selectinload
from models import (
FinancialInput,
Project,
ResourceType,
Role,
Scenario,
ScenarioStatus,
@@ -19,6 +20,7 @@ from models import (
UserRole,
)
from services.exceptions import EntityConflictError, EntityNotFoundError
from services.export_query import ProjectExportFilters, ScenarioExportFilters
class ProjectRepository:
@@ -79,6 +81,52 @@ class ProjectRepository:
records = self.session.execute(stmt).scalars().all()
return {project.name.lower(): project for project in records}
def filtered_for_export(
self,
filters: ProjectExportFilters | None = None,
*,
include_scenarios: bool = False,
) -> Sequence[Project]:
stmt = select(Project)
if include_scenarios:
stmt = stmt.options(selectinload(Project.scenarios))
if filters:
ids = filters.normalised_ids()
if ids:
stmt = stmt.where(Project.id.in_(ids))
name_matches = filters.normalised_names()
if name_matches:
stmt = stmt.where(func.lower(Project.name).in_(name_matches))
name_pattern = filters.name_search_pattern()
if name_pattern:
stmt = stmt.where(Project.name.ilike(name_pattern))
locations = filters.normalised_locations()
if locations:
stmt = stmt.where(func.lower(Project.location).in_(locations))
if filters.operation_types:
stmt = stmt.where(Project.operation_type.in_(
filters.operation_types))
if filters.created_from:
stmt = stmt.where(Project.created_at >= filters.created_from)
if filters.created_to:
stmt = stmt.where(Project.created_at <= filters.created_to)
if filters.updated_from:
stmt = stmt.where(Project.updated_at >= filters.updated_from)
if filters.updated_to:
stmt = stmt.where(Project.updated_at <= filters.updated_to)
stmt = stmt.order_by(Project.name, Project.id)
return self.session.execute(stmt).scalars().all()
def delete(self, project_id: int) -> None:
project = self.get(project_id)
self.session.delete(project)
@@ -177,6 +225,76 @@ class ScenarioRepository:
records = self.session.execute(stmt).scalars().all()
return {scenario.name.lower(): scenario for scenario in records}
def filtered_for_export(
self,
filters: ScenarioExportFilters | None = None,
*,
include_project: bool = True,
) -> Sequence[Scenario]:
stmt = select(Scenario)
if include_project:
stmt = stmt.options(joinedload(Scenario.project))
if filters:
scenario_ids = filters.normalised_ids()
if scenario_ids:
stmt = stmt.where(Scenario.id.in_(scenario_ids))
project_ids = filters.normalised_project_ids()
if project_ids:
stmt = stmt.where(Scenario.project_id.in_(project_ids))
project_names = filters.normalised_project_names()
if project_names:
project_id_select = select(Project.id).where(
func.lower(Project.name).in_(project_names)
)
stmt = stmt.where(Scenario.project_id.in_(project_id_select))
name_pattern = filters.name_search_pattern()
if name_pattern:
stmt = stmt.where(Scenario.name.ilike(name_pattern))
if filters.statuses:
stmt = stmt.where(Scenario.status.in_(filters.statuses))
if filters.start_date_from:
stmt = stmt.where(Scenario.start_date >=
filters.start_date_from)
if filters.start_date_to:
stmt = stmt.where(Scenario.start_date <= filters.start_date_to)
if filters.end_date_from:
stmt = stmt.where(Scenario.end_date >= filters.end_date_from)
if filters.end_date_to:
stmt = stmt.where(Scenario.end_date <= filters.end_date_to)
if filters.created_from:
stmt = stmt.where(Scenario.created_at >= filters.created_from)
if filters.created_to:
stmt = stmt.where(Scenario.created_at <= filters.created_to)
if filters.updated_from:
stmt = stmt.where(Scenario.updated_at >= filters.updated_from)
if filters.updated_to:
stmt = stmt.where(Scenario.updated_at <= filters.updated_to)
currencies = filters.normalised_currencies()
if currencies:
stmt = stmt.where(func.upper(
Scenario.currency).in_(currencies))
if filters.primary_resources:
stmt = stmt.where(Scenario.primary_resource.in_(
filters.primary_resources))
stmt = stmt.order_by(Scenario.name, Scenario.id)
return self.session.execute(stmt).scalars().all()
def delete(self, scenario_id: int) -> None:
scenario = self.get(scenario_id)
self.session.delete(scenario)