144 lines
4.4 KiB
Python
144 lines
4.4 KiB
Python
from __future__ import annotations
|
|
|
|
from io import BytesIO
|
|
|
|
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status
|
|
|
|
from dependencies import get_import_ingestion_service, require_roles
|
|
from models import User
|
|
from schemas.imports import (
|
|
ImportCommitRequest,
|
|
ProjectImportCommitResponse,
|
|
ProjectImportPreviewResponse,
|
|
ScenarioImportCommitResponse,
|
|
ScenarioImportPreviewResponse,
|
|
)
|
|
from services.importers import ImportIngestionService, UnsupportedImportFormat
|
|
|
|
router = APIRouter(prefix="/imports", tags=["Imports"])
|
|
|
|
MANAGE_ROLES = ("project_manager", "admin")
|
|
|
|
|
|
async def _read_upload_file(upload: UploadFile) -> BytesIO:
|
|
content = await upload.read()
|
|
if not content:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Uploaded file is empty.",
|
|
)
|
|
return BytesIO(content)
|
|
|
|
|
|
@router.post(
|
|
"/projects/preview",
|
|
response_model=ProjectImportPreviewResponse,
|
|
status_code=status.HTTP_200_OK,
|
|
)
|
|
async def preview_project_import(
|
|
file: UploadFile = File(...,
|
|
description="Project import file (CSV or Excel)"),
|
|
_: User = Depends(require_roles(*MANAGE_ROLES)),
|
|
ingestion_service: ImportIngestionService = Depends(
|
|
get_import_ingestion_service),
|
|
) -> ProjectImportPreviewResponse:
|
|
if not file.filename:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Filename is required for import.",
|
|
)
|
|
|
|
stream = await _read_upload_file(file)
|
|
|
|
try:
|
|
preview = ingestion_service.preview_projects(stream, file.filename)
|
|
except UnsupportedImportFormat as exc:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=str(exc),
|
|
) from exc
|
|
|
|
return ProjectImportPreviewResponse.model_validate(preview)
|
|
|
|
|
|
@router.post(
|
|
"/scenarios/preview",
|
|
response_model=ScenarioImportPreviewResponse,
|
|
status_code=status.HTTP_200_OK,
|
|
)
|
|
async def preview_scenario_import(
|
|
file: UploadFile = File(...,
|
|
description="Scenario import file (CSV or Excel)"),
|
|
_: User = Depends(require_roles(*MANAGE_ROLES)),
|
|
ingestion_service: ImportIngestionService = Depends(
|
|
get_import_ingestion_service),
|
|
) -> ScenarioImportPreviewResponse:
|
|
if not file.filename:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Filename is required for import.",
|
|
)
|
|
|
|
stream = await _read_upload_file(file)
|
|
|
|
try:
|
|
preview = ingestion_service.preview_scenarios(stream, file.filename)
|
|
except UnsupportedImportFormat as exc:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=str(exc),
|
|
) from exc
|
|
|
|
return ScenarioImportPreviewResponse.model_validate(preview)
|
|
|
|
|
|
def _value_error_status(exc: ValueError) -> int:
|
|
detail = str(exc)
|
|
if detail.lower().startswith("unknown"):
|
|
return status.HTTP_404_NOT_FOUND
|
|
return status.HTTP_400_BAD_REQUEST
|
|
|
|
|
|
@router.post(
|
|
"/projects/commit",
|
|
response_model=ProjectImportCommitResponse,
|
|
status_code=status.HTTP_200_OK,
|
|
)
|
|
async def commit_project_import_endpoint(
|
|
payload: ImportCommitRequest,
|
|
_: User = Depends(require_roles(*MANAGE_ROLES)),
|
|
ingestion_service: ImportIngestionService = Depends(
|
|
get_import_ingestion_service),
|
|
) -> ProjectImportCommitResponse:
|
|
try:
|
|
result = ingestion_service.commit_project_import(payload.token)
|
|
except ValueError as exc:
|
|
raise HTTPException(
|
|
status_code=_value_error_status(exc),
|
|
detail=str(exc),
|
|
) from exc
|
|
|
|
return ProjectImportCommitResponse.model_validate(result)
|
|
|
|
|
|
@router.post(
|
|
"/scenarios/commit",
|
|
response_model=ScenarioImportCommitResponse,
|
|
status_code=status.HTTP_200_OK,
|
|
)
|
|
async def commit_scenario_import_endpoint(
|
|
payload: ImportCommitRequest,
|
|
_: User = Depends(require_roles(*MANAGE_ROLES)),
|
|
ingestion_service: ImportIngestionService = Depends(
|
|
get_import_ingestion_service),
|
|
) -> ScenarioImportCommitResponse:
|
|
try:
|
|
result = ingestion_service.commit_scenario_import(payload.token)
|
|
except ValueError as exc:
|
|
raise HTTPException(
|
|
status_code=_value_error_status(exc),
|
|
detail=str(exc),
|
|
) from exc
|
|
|
|
return ScenarioImportCommitResponse.model_validate(result)
|