Ryan Malloy 44ed9936b7 Initial commit: Claude Code Project Tracker
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>
2025-08-11 02:59:21 -06:00

220 lines
8.1 KiB
Python

"""
Pydantic schemas for API request/response models.
"""
from datetime import datetime
from typing import Optional, List, Dict, Any, Union
from pydantic import BaseModel, Field
# Base schemas
class TimestampMixin(BaseModel):
"""Mixin for timestamp fields."""
created_at: datetime
updated_at: datetime
# Session schemas
class SessionStartRequest(BaseModel):
"""Request schema for starting a session."""
session_type: str = Field(..., description="Type of session: startup, resume, or clear")
working_directory: str = Field(..., description="Current working directory")
git_branch: Optional[str] = Field(None, description="Current git branch")
git_repo: Optional[str] = Field(None, description="Git repository URL")
environment: Optional[Dict[str, Any]] = Field(None, description="Environment variables and context")
class SessionEndRequest(BaseModel):
"""Request schema for ending a session."""
session_id: int = Field(..., description="ID of the session to end")
end_reason: str = Field(default="normal", description="Reason for ending: normal, interrupted, or timeout")
class SessionResponse(BaseModel):
"""Response schema for session operations."""
session_id: int
project_id: int
status: str
message: Optional[str] = None
class Config:
from_attributes = True
# Conversation schemas
class ConversationRequest(BaseModel):
"""Request schema for logging conversations."""
session_id: int = Field(..., description="Associated session ID")
timestamp: datetime = Field(default_factory=datetime.utcnow, description="When the exchange occurred")
user_prompt: Optional[str] = Field(None, description="User's input message")
claude_response: Optional[str] = Field(None, description="Claude's response")
tools_used: Optional[List[str]] = Field(None, description="Tools used in the response")
files_affected: Optional[List[str]] = Field(None, description="Files mentioned or modified")
context: Optional[Dict[str, Any]] = Field(None, description="Additional context")
tokens_input: Optional[int] = Field(None, description="Estimated input token count")
tokens_output: Optional[int] = Field(None, description="Estimated output token count")
exchange_type: str = Field(..., description="Type of exchange: user_prompt or claude_response")
class ConversationResponse(BaseModel):
"""Response schema for conversation operations."""
id: int
session_id: int
timestamp: datetime
exchange_type: str
content_length: int
class Config:
from_attributes = True
# Activity schemas
class ActivityRequest(BaseModel):
"""Request schema for recording activities."""
session_id: int = Field(..., description="Associated session ID")
conversation_id: Optional[int] = Field(None, description="Associated conversation ID")
timestamp: datetime = Field(default_factory=datetime.utcnow, description="When the activity occurred")
tool_name: str = Field(..., description="Name of the tool used")
action: str = Field(..., description="Specific action taken")
file_path: Optional[str] = Field(None, description="Target file path if applicable")
metadata: Optional[Dict[str, Any]] = Field(None, description="Tool-specific metadata")
success: bool = Field(default=True, description="Whether the operation succeeded")
error_message: Optional[str] = Field(None, description="Error details if failed")
lines_added: Optional[int] = Field(None, description="Lines added for Edit/Write operations")
lines_removed: Optional[int] = Field(None, description="Lines removed for Edit operations")
class ActivityResponse(BaseModel):
"""Response schema for activity operations."""
id: int
session_id: int
tool_name: str
action: str
timestamp: datetime
success: bool
class Config:
from_attributes = True
# Waiting period schemas
class WaitingStartRequest(BaseModel):
"""Request schema for starting a waiting period."""
session_id: int = Field(..., description="Associated session ID")
timestamp: datetime = Field(default_factory=datetime.utcnow, description="When waiting started")
context_before: Optional[str] = Field(None, description="Context before waiting")
class WaitingEndRequest(BaseModel):
"""Request schema for ending a waiting period."""
session_id: int = Field(..., description="Associated session ID")
timestamp: datetime = Field(default_factory=datetime.utcnow, description="When waiting ended")
duration_seconds: Optional[int] = Field(None, description="Total waiting duration")
context_after: Optional[str] = Field(None, description="Context after waiting")
class WaitingPeriodResponse(BaseModel):
"""Response schema for waiting period operations."""
id: int
session_id: int
start_time: datetime
end_time: Optional[datetime]
duration_seconds: Optional[int]
engagement_score: float
class Config:
from_attributes = True
# Git operation schemas
class GitOperationRequest(BaseModel):
"""Request schema for git operations."""
session_id: int = Field(..., description="Associated session ID")
timestamp: datetime = Field(default_factory=datetime.utcnow, description="When the operation occurred")
operation: str = Field(..., description="Type of git operation")
command: str = Field(..., description="Full git command executed")
result: Optional[str] = Field(None, description="Command output")
success: bool = Field(default=True, description="Whether the command succeeded")
files_changed: Optional[List[str]] = Field(None, description="Files affected by the operation")
lines_added: Optional[int] = Field(None, description="Lines added")
lines_removed: Optional[int] = Field(None, description="Lines removed")
commit_hash: Optional[str] = Field(None, description="Git commit SHA")
branch_from: Optional[str] = Field(None, description="Source branch")
branch_to: Optional[str] = Field(None, description="Target branch")
class GitOperationResponse(BaseModel):
"""Response schema for git operations."""
id: int
session_id: int
operation: str
timestamp: datetime
success: bool
commit_hash: Optional[str]
class Config:
from_attributes = True
# Project schemas
class ProjectSummary(BaseModel):
"""Summary information about a project."""
id: int
name: str
path: str
git_repo: Optional[str]
languages: Optional[List[str]]
total_sessions: int
total_time_minutes: int
last_activity: Optional[datetime]
files_modified_count: int
lines_changed_count: int
class Config:
from_attributes = True
class TimelineEvent(BaseModel):
"""Individual event in project timeline."""
timestamp: datetime
type: str # session_start, session_end, conversation, activity, git_operation
data: Dict[str, Any]
class ProjectTimeline(BaseModel):
"""Project timeline with events."""
project: ProjectSummary
timeline: List[TimelineEvent]
# Analytics schemas
class ProductivityMetrics(BaseModel):
"""Productivity analytics response."""
engagement_score: float = Field(..., description="Overall engagement level (0-100)")
average_session_length: float = Field(..., description="Minutes per session")
think_time_average: float = Field(..., description="Average waiting time between interactions")
files_per_session: float = Field(..., description="Average files touched per session")
tools_most_used: List[Dict[str, Union[str, int]]] = Field(..., description="Most frequently used tools")
productivity_trends: List[Dict[str, Union[str, float]]] = Field(..., description="Daily productivity scores")
class ConversationSearchResult(BaseModel):
"""Search result for conversation search."""
id: int
project_name: str
timestamp: datetime
user_prompt: Optional[str]
claude_response: Optional[str]
relevance_score: float
context: List[str]
class Config:
from_attributes = True
# Error schemas
class ErrorResponse(BaseModel):
"""Standard error response."""
error: str
message: str
details: Optional[Dict[str, Any]] = None