Add comprehensive development intelligence system that tracks: - Development sessions with automatic start/stop - Full conversation history with semantic search - Tool usage and file operation analytics - Think time and engagement analysis - Git activity correlation - Learning pattern recognition - Productivity insights and metrics Features: - FastAPI backend with SQLite database - Modern web dashboard with interactive charts - Claude Code hook integration for automatic tracking - Comprehensive test suite with 100+ tests - Complete API documentation (OpenAPI/Swagger) - Privacy-first design with local data storage 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
69 lines
2.6 KiB
Python
69 lines
2.6 KiB
Python
"""
|
|
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)
|
|
|
|
# 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) -> 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.last_session = func.now() |