Initial commit
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
class DomainError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ModelNotFoundError(DomainError):
|
||||
pass
|
||||
|
||||
|
||||
class DuplicateModelVersionError(DomainError):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidModelVersionError(DomainError):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidModelFileError(DomainError):
|
||||
pass
|
||||
|
||||
|
||||
class ModelFileMissingError(DomainError):
|
||||
pass
|
||||
|
||||
|
||||
class ModelConversionError(DomainError):
|
||||
pass
|
||||
@@ -0,0 +1,12 @@
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class Model:
|
||||
id: int | None
|
||||
version: str
|
||||
filename: str
|
||||
file_path: str
|
||||
created_at: datetime
|
||||
is_latest: bool
|
||||
@@ -0,0 +1,8 @@
|
||||
from abc import abstractmethod, ABC
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class ModelConverter(ABC):
|
||||
@abstractmethod
|
||||
def convert(self, model_pt_path: Path, results_dir: Path) -> Path:
|
||||
raise NotImplementedError
|
||||
@@ -0,0 +1,17 @@
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
from app.domain.Model import Model
|
||||
|
||||
|
||||
class ModelRepository(ABC):
|
||||
@abstractmethod
|
||||
def get_latest(self) -> Model | None:
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def get_by_version(self, version: str) -> Model | None:
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def add(self, model: Model) -> Model:
|
||||
raise NotImplementedError
|
||||
@@ -0,0 +1,41 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class ModelStorage(ABC):
|
||||
@property
|
||||
@abstractmethod
|
||||
def models_dir(self) -> Path:
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def ensure_directories(self) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def validate_upload_filename(self, filename: str | None) -> str:
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def save_upload_to_tmp(self, source_file, filename: str) -> Path:
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def build_model_filename(self, version: str) -> str:
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def prepare_conversion_input(self, tmp_path: Path, model_filename: str) -> Path:
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def finalize_converted_model(self, converted_path: Path, model_filename: str) -> Path:
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def ensure_model_file_exists(self, file_path: str) -> Path:
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def delete_file_if_exists(self, file_path: Path | None) -> None:
|
||||
raise NotImplementedError
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
from app.domain.ModelStorage import ModelStorage
|
||||
from app.domain.Model import Model
|
||||
from app.domain.Exception import ModelNotFoundError
|
||||
from app.domain.ModelRepository import ModelRepository
|
||||
|
||||
|
||||
class GetLatestModelUseCase:
|
||||
def __init__(self, repository: ModelRepository, storage: ModelStorage) -> None:
|
||||
self._repository = repository
|
||||
self._storage = storage
|
||||
|
||||
def execute(self, version: str | None = None, filename: str | None = None) -> Model:
|
||||
model = self._repository.get_latest()
|
||||
if model is None:
|
||||
raise ModelNotFoundError("No models have been uploaded yet.")
|
||||
if version is not None and model.version != version:
|
||||
raise ModelNotFoundError("Latest model does not match requested version.")
|
||||
if filename is not None and model.filename != filename:
|
||||
raise ModelNotFoundError("Latest model does not match requested filename.")
|
||||
self._storage.ensure_model_file_exists(model.file_path)
|
||||
return model
|
||||
@@ -0,0 +1,74 @@
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from app.domain.Model import Model
|
||||
from app.domain.Exception import *
|
||||
from app.domain.ModelRepository import ModelRepository
|
||||
from app.domain.ModelStorage import ModelStorage
|
||||
from app.domain.ModelConverter import ModelConverter
|
||||
|
||||
|
||||
class UploadModelUseCase:
|
||||
def __init__(
|
||||
self,
|
||||
repository: ModelRepository,
|
||||
storage: ModelStorage,
|
||||
converter: ModelConverter,
|
||||
) -> None:
|
||||
self._repository = repository
|
||||
self._storage = storage
|
||||
self._converter = converter
|
||||
|
||||
def execute(self, version: str, upload_file) -> Model:
|
||||
normalized_version = version.strip()
|
||||
if not normalized_version:
|
||||
raise InvalidModelVersionError("Version must not be empty.")
|
||||
if self._repository.get_by_version(normalized_version) is not None:
|
||||
raise DuplicateModelVersionError(f"Model version '{normalized_version}' already exists.")
|
||||
|
||||
safe_filename = self._storage.validate_upload_filename(upload_file.filename)
|
||||
tmp_path = self._storage.save_upload_to_tmp(upload_file.file, safe_filename)
|
||||
conversion_input_path = None
|
||||
final_model_path = None
|
||||
|
||||
try:
|
||||
model_filename = self._storage.build_model_filename(normalized_version)
|
||||
conversion_input_path = self._storage.prepare_conversion_input(
|
||||
tmp_path=tmp_path,
|
||||
model_filename=model_filename,
|
||||
)
|
||||
converted_path = self._converter.convert(
|
||||
model_pt_path=conversion_input_path,
|
||||
results_dir=self._storage.models_dir,
|
||||
)
|
||||
final_model_path = self._storage.finalize_converted_model(
|
||||
converted_path=converted_path,
|
||||
model_filename=model_filename,
|
||||
)
|
||||
|
||||
model = self._repository.add(
|
||||
Model(
|
||||
id=None,
|
||||
version=normalized_version,
|
||||
filename=model_filename,
|
||||
file_path=str(final_model_path),
|
||||
created_at=datetime.now(timezone.utc),
|
||||
is_latest=True,
|
||||
)
|
||||
)
|
||||
return model
|
||||
except Exception as exc:
|
||||
self._storage.delete_file_if_exists(final_model_path)
|
||||
if isinstance(
|
||||
exc,
|
||||
(
|
||||
InvalidModelVersionError,
|
||||
DuplicateModelVersionError,
|
||||
ModelNotFoundError,
|
||||
),
|
||||
):
|
||||
raise
|
||||
raise ModelConversionError(f"Model conversion failed: {exc}") from exc
|
||||
finally:
|
||||
upload_file.file.close()
|
||||
self._storage.delete_file_if_exists(tmp_path)
|
||||
self._storage.delete_file_if_exists(conversion_input_path)
|
||||
Reference in New Issue
Block a user