feat(03-01): create project service module

- list_projects() returns all projects ordered by created_at desc
- create_project() creates project with default models
- get_project() retrieves project by ID

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Mikkel Georgsen 2026-01-16 18:37:21 +00:00
parent e3d72dab60
commit 718dcea7dc
2 changed files with 66 additions and 0 deletions

View file

@ -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"]

View file

@ -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()