diff --git a/src/moai/core/services/__init__.py b/src/moai/core/services/__init__.py new file mode 100644 index 0000000..2b8de7f --- /dev/null +++ b/src/moai/core/services/__init__.py @@ -0,0 +1,9 @@ +"""Service layer for MoAI business logic. + +Services encapsulate database operations and business rules, +providing a clean interface for handlers to use. +""" + +from moai.core.services.project import create_project, get_project, list_projects + +__all__ = ["create_project", "get_project", "list_projects"] diff --git a/src/moai/core/services/project.py b/src/moai/core/services/project.py new file mode 100644 index 0000000..699d5a9 --- /dev/null +++ b/src/moai/core/services/project.py @@ -0,0 +1,57 @@ +"""Project service for MoAI. + +Provides CRUD operations for projects. +""" + +from sqlalchemy import select + +from moai.core.database import get_session +from moai.core.models import Project + +DEFAULT_MODELS = ["claude", "gpt", "gemini"] + + +async def list_projects() -> list[Project]: + """List all projects ordered by creation date (newest first). + + Returns: + List of Project objects. + """ + async with get_session() as session: + result = await session.execute(select(Project).order_by(Project.created_at.desc())) + return list(result.scalars().all()) + + +async def create_project(name: str, models: list[str] | None = None) -> Project: + """Create a new project. + + Args: + name: Human-readable project name. + models: List of AI model identifiers. Defaults to ["claude", "gpt", "gemini"]. + + Returns: + The created Project object. + """ + if models is None: + models = DEFAULT_MODELS.copy() + + async with get_session() as session: + project = Project(name=name, models=models) + session.add(project) + await session.flush() + await session.refresh(project) + return project + + +async def get_project(project_id: str) -> Project | None: + """Get a project by ID. + + Args: + project_id: The project's UUID. + + Returns: + The Project object if found, None otherwise. + """ + async with get_session() as session: + result = await session.execute(select(Project).where(Project.id == project_id)) + return result.scalar_one_or_none()