feat: implement import functionality for projects and scenarios with CSV/XLSX support, including validation and error handling

This commit is contained in:
2025-11-10 09:10:47 +01:00
parent 7058eb4172
commit 3bc124c11f
7 changed files with 1084 additions and 2 deletions

View File

@@ -2,7 +2,7 @@ from __future__ import annotations
from collections.abc import Iterable
from datetime import datetime
from typing import Sequence
from typing import Mapping, Sequence
from sqlalchemy import select, func
from sqlalchemy.exc import IntegrityError
@@ -70,6 +70,15 @@ class ProjectRepository:
"Project violates uniqueness constraints") from exc
return project
def find_by_names(self, names: Iterable[str]) -> Mapping[str, Project]:
normalised = {name.strip().lower()
for name in names if name and name.strip()}
if not normalised:
return {}
stmt = select(Project).where(func.lower(Project.name).in_(normalised))
records = self.session.execute(stmt).scalars().all()
return {project.name.lower(): project for project in records}
def delete(self, project_id: int) -> None:
project = self.get(project_id)
self.session.delete(project)
@@ -149,6 +158,25 @@ class ScenarioRepository:
raise EntityConflictError("Scenario violates constraints") from exc
return scenario
def find_by_project_and_names(
self,
project_id: int,
names: Iterable[str],
) -> Mapping[str, Scenario]:
normalised = {name.strip().lower()
for name in names if name and name.strip()}
if not normalised:
return {}
stmt = (
select(Scenario)
.where(
Scenario.project_id == project_id,
func.lower(Scenario.name).in_(normalised),
)
)
records = self.session.execute(stmt).scalars().all()
return {scenario.name.lower(): scenario for scenario in records}
def delete(self, scenario_id: int) -> None:
scenario = self.get(scenario_id)
self.session.delete(scenario)