""" Project model for tracking development projects. """ from datetime import datetime from typing import Optional, List from sqlalchemy import String, Text, Integer, DateTime, JSON from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.sql import func from .base import Base, TimestampMixin class Project(Base, TimestampMixin): """ Represents a development project tracked by Claude Code. A project is typically identified by its filesystem path and may correspond to a git repository. """ __tablename__ = "projects" # Primary key id: Mapped[int] = mapped_column(primary_key=True) # Core project information name: Mapped[str] = mapped_column(String(255), nullable=False) path: Mapped[str] = mapped_column(Text, nullable=False, unique=True, index=True) git_repo: Mapped[Optional[str]] = mapped_column(String(500), nullable=True) languages: Mapped[Optional[List[str]]] = mapped_column(JSON, nullable=True) # Activity tracking last_session: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True, index=True) total_sessions: Mapped[int] = mapped_column(Integer, default=0, nullable=False) total_time_minutes: Mapped[int] = mapped_column(Integer, default=0, nullable=False) files_modified_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False) lines_changed_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False) tool_calls_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False) # Relationships sessions: Mapped[List["Session"]] = relationship( "Session", back_populates="project", cascade="all, delete-orphan", order_by="Session.start_time.desc()" ) def __repr__(self) -> str: return f"" @property def is_git_repo(self) -> bool: """Check if this project is a git repository.""" return self.git_repo is not None and self.git_repo != "" @property def primary_language(self) -> Optional[str]: """Get the primary programming language for this project.""" if self.languages and len(self.languages) > 0: return self.languages[0] return None def update_stats(self, session_duration_minutes: int, files_count: int, lines_count: int, tool_calls_count: int = 0) -> None: """Update project statistics after a session.""" self.total_sessions += 1 self.total_time_minutes += session_duration_minutes self.files_modified_count += files_count self.lines_changed_count += lines_count self.tool_calls_count += tool_calls_count self.last_session = func.now()