"""Project service for project management.""" import uuid from datetime import datetime from pathlib import Path from typing import Optional import yaml from ..models import Project _PROJECTS_ROOT = Path("projects") def get_projects_root() -> Path: """Return the root directory for all projects.""" return _PROJECTS_ROOT def _get_project_meta_path(slug: str) -> Path: """Return the path to the project's meta/project.yaml file.""" return _PROJECTS_ROOT / slug / "meta" / "project.yaml" def _get_project_readme_path(slug: str) -> Path: """Return the path to the project's README.md file.""" return _PROJECTS_ROOT / slug / "README.md" def create_project( name: str, slug: str, description: str = "", type: str = "misc", tags: Optional[list[str]] = None, repo_path: Optional[Path] = None, ) -> Project: """ Create a new project and return the Project instance. Note: This does not write any files - that is handled by storage. """ if tags is None: tags = [] project = Project( id=str(uuid.uuid4()), name=name, slug=slug, description=description, type=type, status="inbox", tags=tags, root_path=str(_PROJECTS_ROOT / slug), repo_path=str(repo_path) if repo_path else None, created_at=datetime.now(), updated_at=datetime.now(), ) return project def get_project(slug: str) -> Optional[Project]: """ Get a project by slug. Reads from meta/project.yaml in the project directory. """ meta_path = _get_project_meta_path(slug) if not meta_path.exists(): return None try: with open(meta_path, "r", encoding="utf-8") as f: data = yaml.safe_load(f) if data: return Project(**data) except (yaml.YAMLError, TypeError): pass return None def update_project(slug: str, **kwargs) -> Optional[Project]: """ Update a project's attributes. Note: This does not persist - that is handled by storage. """ project = get_project(slug) if project is None: return None for key, value in kwargs.items(): if hasattr(project, key): setattr(project, key, value) project.updated_at = datetime.now() return project def list_projects() -> list[Project]: """ List all projects. Note: This reads from file system - placeholder for storage integration. """ projects_root = get_projects_root() if not projects_root.exists(): return [] projects = [] for item in projects_root.iterdir(): if item.is_dir() and not item.name.startswith("."): project = get_project(item.name) if project is not None: projects.append(project) return projects def ensure_project_structure(slug: str) -> None: """ Ensure the project directory structure exists. Creates: sessions/, docs/, assets/, meta/ Note: This creates directories only - actual file writing is storage's job. """ project_root = _PROJECTS_ROOT / slug directories = ["sessions", "docs", "assets", "meta"] for directory in directories: (project_root / directory).mkdir(parents=True, exist_ok=True)