Source code for todowrite.utils.database_utils

from __future__ import annotations

"""Utility functions for database naming and path management."""

import os
from pathlib import Path


[docs] def find_project_root() -> str: """Find the project root (monorepo root) by looking for package structure. Searches up the directory tree for a directory containing lib_package, cli_package, and web_package subdirectories. Returns: Path to project root directory, or current directory if not found """ search_dir = Path.cwd() # Search up the directory tree for project root # (contains lib_package, cli_package, web_package) while str(search_dir) != "/": if ( (search_dir / "lib_package").exists() and (search_dir / "cli_package").exists() and (search_dir / "web_package").exists() ): return str(search_dir) search_dir = search_dir.parent # If not found, return current directory # (fallback for non-monorepo projects) return str(Path.cwd())
[docs] def get_project_name() -> str: """Get the current project name based on project root detection.""" # Try to find project root first project_root = find_project_root() project_name = Path(project_root).name # If we found a project root that's different from current directory, # use its name if project_root != str(Path.cwd()): # We're in a package directory, use the project root name pass # project_name already set correctly else: # Fallback to current directory name for non-monorepo projects project_name = Path.cwd().name # If directory name is generic or empty, use a timestamp-based fallback generic_names = {"", ".", "src", "lib", "app", "project", "home"} if project_name in generic_names or not project_name: from datetime import datetime project_name = f"project_{datetime.now().strftime('%Y%m%d_%H%M%S')}" # Sanitize project name for filesystem project_name = project_name.replace(" ", "_").replace("-", "_") project_name = "".join(c for c in project_name if c.isalnum() or c == "_") return project_name
[docs] def get_project_database_name( environment: str = "development", project_name: str | None = None ) -> str: """Generate a project-specific database name for the given environment. Args: environment: The environment type (development, testing, production) project_name: Optional project name override. If None, detected from CWD. Returns: Database filename with project-specific naming. """ if project_name is None: project_name = get_project_name() # Avoid redundant prefix when project name contains "todowrite" # Example: todowrite_development.db # (not todowrite_todowrite_development.db) if project_name.lower() == "todowrite": return f"todowrite_{environment}.db" else: return f"todowrite_{project_name}_{environment}.db"
[docs] def get_database_path( environment: str = "development", base_dir: str | None = None, project_name: str | None = None, ) -> str: """Get a full database path with project-specific naming. Args: environment: The environment type (development, testing, production) base_dir: Base directory for databases. Defaults to ~/dbs project_name: Optional project name override. Returns: Full path to the database file. """ if project_name is None: project_name = get_project_name() db_name = get_project_database_name(environment, project_name) # Handle different environments with different base directories if environment == "testing": # Testing databases go in project_root/tmp project_root = Path(find_project_root()) tmp_dir = project_root / "tmp" tmp_dir.mkdir(exist_ok=True) db_path = tmp_dir / db_name elif environment == "production": # Production databases go in ~/dbs if base_dir is None: base_dir = "~/dbs" db_path = os.path.join(os.path.expanduser(base_dir), db_name) else: # Development databases go in ~/dbs if base_dir is None: base_dir = "~/dbs" db_path = os.path.join(os.path.expanduser(base_dir), db_name) return str(db_path)