Add comprehensive documentation system and tool call tracking
## 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>
This commit is contained in:
parent
166247bf70
commit
bec1606c86
27 changed files with 6150 additions and 32 deletions
|
|
@ -9,6 +9,7 @@ from .conversation import Conversation
|
|||
from .activity import Activity
|
||||
from .waiting_period import WaitingPeriod
|
||||
from .git_operation import GitOperation
|
||||
from .tool_call import ToolCall
|
||||
|
||||
__all__ = [
|
||||
"Base",
|
||||
|
|
@ -18,4 +19,5 @@ __all__ = [
|
|||
"Activity",
|
||||
"WaitingPeriod",
|
||||
"GitOperation",
|
||||
"ToolCall",
|
||||
]
|
||||
|
|
@ -46,7 +46,7 @@ class Activity(Base, TimestampMixin):
|
|||
file_path: Mapped[Optional[str]] = mapped_column(Text, nullable=True, index=True)
|
||||
|
||||
# Metadata and results
|
||||
metadata: Mapped[Optional[Dict[str, Any]]] = mapped_column(JSON, nullable=True)
|
||||
activity_metadata: Mapped[Optional[Dict[str, Any]]] = mapped_column(JSON, nullable=True)
|
||||
success: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
||||
error_message: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
||||
|
||||
|
|
@ -145,18 +145,18 @@ class Activity(Base, TimestampMixin):
|
|||
|
||||
def get_command_executed(self) -> Optional[str]:
|
||||
"""Get the command that was executed (for Bash activities)."""
|
||||
if self.tool_name == "Bash" and self.metadata:
|
||||
return self.metadata.get("command")
|
||||
if self.tool_name == "Bash" and self.activity_metadata:
|
||||
return self.activity_metadata.get("command")
|
||||
return None
|
||||
|
||||
def get_search_pattern(self) -> Optional[str]:
|
||||
"""Get the search pattern (for Grep activities)."""
|
||||
if self.tool_name == "Grep" and self.metadata:
|
||||
return self.metadata.get("pattern")
|
||||
if self.tool_name == "Grep" and self.activity_metadata:
|
||||
return self.activity_metadata.get("pattern")
|
||||
return None
|
||||
|
||||
def get_task_type(self) -> Optional[str]:
|
||||
"""Get the task type (for Task activities)."""
|
||||
if self.tool_name == "Task" and self.metadata:
|
||||
return self.metadata.get("task_type")
|
||||
if self.tool_name == "Task" and self.activity_metadata:
|
||||
return self.activity_metadata.get("task_type")
|
||||
return None
|
||||
|
|
@ -36,6 +36,7 @@ class Project(Base, TimestampMixin):
|
|||
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(
|
||||
|
|
@ -60,10 +61,11 @@ class Project(Base, TimestampMixin):
|
|||
return self.languages[0]
|
||||
return None
|
||||
|
||||
def update_stats(self, session_duration_minutes: int, files_count: int, lines_count: int) -> 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()
|
||||
|
|
@ -74,6 +74,12 @@ class Session(Base, TimestampMixin):
|
|||
cascade="all, delete-orphan",
|
||||
order_by="GitOperation.timestamp"
|
||||
)
|
||||
tool_calls: Mapped[List["ToolCall"]] = relationship(
|
||||
"ToolCall",
|
||||
back_populates="session",
|
||||
cascade="all, delete-orphan",
|
||||
order_by="ToolCall.timestamp"
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<Session(id={self.id}, project_id={self.project_id}, type='{self.session_type}')>"
|
||||
|
|
@ -111,10 +117,12 @@ class Session(Base, TimestampMixin):
|
|||
(activity.lines_added or 0) + (activity.lines_removed or 0)
|
||||
for activity in self.activities
|
||||
)
|
||||
total_tool_calls = len(self.tool_calls)
|
||||
self.project.update_stats(
|
||||
session_duration_minutes=self.duration_minutes or 0,
|
||||
files_count=unique_files,
|
||||
lines_count=total_lines
|
||||
lines_count=total_lines,
|
||||
tool_calls_count=total_tool_calls
|
||||
)
|
||||
|
||||
def add_activity(self) -> None:
|
||||
|
|
|
|||
33
app/models/tool_call.py
Normal file
33
app/models/tool_call.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
"""
|
||||
Tool call tracking model for Claude Code sessions.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey, Boolean
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from app.models.base import Base
|
||||
|
||||
|
||||
class ToolCall(Base):
|
||||
"""
|
||||
Tracks individual tool calls made during Claude Code sessions.
|
||||
|
||||
This helps analyze which tools are used most frequently and their success rates.
|
||||
"""
|
||||
__tablename__ = "tool_calls"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
session_id = Column(String, ForeignKey("sessions.id"), nullable=False, index=True)
|
||||
tool_name = Column(String(100), nullable=False, index=True)
|
||||
parameters = Column(Text, nullable=True) # JSON string of tool parameters
|
||||
result_status = Column(String(20), nullable=True, index=True) # success, error, timeout
|
||||
error_message = Column(Text, nullable=True)
|
||||
execution_time_ms = Column(Integer, nullable=True) # Tool execution time in milliseconds
|
||||
timestamp = Column(DateTime, nullable=False, default=datetime.utcnow, index=True)
|
||||
|
||||
# Relationships
|
||||
session = relationship("Session", back_populates="tool_calls")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<ToolCall(id={self.id}, tool={self.tool_name}, session={self.session_id})>"
|
||||
Loading…
Add table
Add a link
Reference in a new issue