## Documentation System - Create complete documentation hub at /dashboard/docs with: - Getting Started guide with quick setup and troubleshooting - Hook Setup Guide with platform-specific configurations - API Reference with all endpoints and examples - FAQ with searchable questions and categories - Add responsive design with interactive features - Update navigation in base template ## Tool Call Tracking - Add ToolCall model for tracking Claude Code tool usage - Create /api/tool-calls endpoints for recording and analytics - Add tool_call hook type with auto-session detection - Include tool calls in project statistics and recalculation - Track tool names, parameters, execution time, and success rates ## Project Enhancements - Add project timeline and statistics pages (fix 404 errors) - Create recalculation script for fixing zero statistics - Update project stats to include tool call counts - Enhance session model with tool call relationships ## Infrastructure - Switch from requirements.txt to pyproject.toml/uv.lock - Add data import functionality for claude.json files - Update database connection to include all new models - Add comprehensive API documentation 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
71 lines
2.8 KiB
Python
71 lines
2.8 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)
|
|
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() |