claude-code-tracker/app/models/project.py

71 lines
2.8 KiB
Python
Raw Permalink Normal View History

"""
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"<Project(id={self.id}, name='{self.name}', path='{self.path}')>"
@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()