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

225 lines
10 KiB
Python
Raw Normal View History

"""
Comprehensive hook tracking models for Claude Code sessions.
"""
from datetime import datetime
from typing import Optional, Dict, Any
from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey, Boolean, Float, JSON
from sqlalchemy.orm import relationship
from app.models.base import Base
class HookEvent(Base):
"""
Base model for all hook events with common fields.
"""
__tablename__ = "hook_events"
id = Column(Integer, primary_key=True, index=True)
session_id = Column(String, ForeignKey("sessions.id"), nullable=False, index=True)
hook_type = Column(String(50), nullable=False, index=True)
timestamp = Column(DateTime, nullable=False, default=datetime.utcnow, index=True)
data = Column(JSON, nullable=True) # Flexible JSON data for hook-specific information
# Relationships
session = relationship("Session", back_populates="hook_events")
def __repr__(self):
return f"<HookEvent(id={self.id}, type={self.hook_type}, session={self.session_id})>"
class ToolError(Base):
"""Track failed tool calls with error details."""
__tablename__ = "tool_errors"
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)
error_type = Column(String(100), nullable=False, index=True)
error_message = Column(Text, nullable=False)
stack_trace = Column(Text, nullable=True)
parameters = Column(JSON, nullable=True)
timestamp = Column(DateTime, nullable=False, default=datetime.utcnow, index=True)
session = relationship("Session")
def __repr__(self):
return f"<ToolError(id={self.id}, tool={self.tool_name}, error={self.error_type})>"
class WaitingPeriodNew(Base):
"""Track periods when Claude is thinking vs actively working."""
__tablename__ = "waiting_periods_new"
id = Column(Integer, primary_key=True, index=True)
session_id = Column(String, ForeignKey("sessions.id"), nullable=False, index=True)
start_time = Column(DateTime, nullable=False, default=datetime.utcnow, index=True)
end_time = Column(DateTime, nullable=True)
duration_ms = Column(Integer, nullable=True)
reason = Column(String(100), nullable=True, index=True) # thinking, processing, waiting_for_input
context = Column(Text, nullable=True) # What was happening when waiting started
session = relationship("Session")
def __repr__(self):
return f"<WaitingPeriodNew(id={self.id}, reason={self.reason}, duration={self.duration_ms}ms)>"
class PerformanceMetric(Base):
"""Monitor system performance during development."""
__tablename__ = "performance_metrics"
id = Column(Integer, primary_key=True, index=True)
session_id = Column(String, ForeignKey("sessions.id"), nullable=False, index=True)
metric_type = Column(String(50), nullable=False, index=True) # memory, cpu, disk
value = Column(Float, nullable=False)
unit = Column(String(20), nullable=False) # MB, %, seconds
threshold_exceeded = Column(Boolean, default=False)
timestamp = Column(DateTime, nullable=False, default=datetime.utcnow, index=True)
session = relationship("Session")
def __repr__(self):
return f"<PerformanceMetric(id={self.id}, type={self.metric_type}, value={self.value}{self.unit})>"
class CodeQualityEvent(Base):
"""Track code quality analysis, linting, and testing."""
__tablename__ = "code_quality_events"
id = Column(Integer, primary_key=True, index=True)
session_id = Column(String, ForeignKey("sessions.id"), nullable=False, index=True)
event_type = Column(String(50), nullable=False, index=True) # lint, format, test, build, analysis
file_path = Column(Text, nullable=True)
tool_name = Column(String(100), nullable=True) # eslint, prettier, pytest, etc.
status = Column(String(20), nullable=False, index=True) # success, warning, error
issues_count = Column(Integer, default=0)
details = Column(JSON, nullable=True) # Specific issues, test results, etc.
duration_ms = Column(Integer, nullable=True)
timestamp = Column(DateTime, nullable=False, default=datetime.utcnow, index=True)
session = relationship("Session")
def __repr__(self):
return f"<CodeQualityEvent(id={self.id}, type={self.event_type}, status={self.status})>"
class WorkflowEvent(Base):
"""Track development workflow patterns."""
__tablename__ = "workflow_events"
id = Column(Integer, primary_key=True, index=True)
session_id = Column(String, ForeignKey("sessions.id"), nullable=False, index=True)
event_type = Column(String(50), nullable=False, index=True) # context_switch, search_query, browser_tab, etc.
description = Column(Text, nullable=True)
event_metadata = Column(JSON, nullable=True) # Event-specific data
source = Column(String(100), nullable=True) # Where the event came from
duration_ms = Column(Integer, nullable=True) # For events with duration
timestamp = Column(DateTime, nullable=False, default=datetime.utcnow, index=True)
session = relationship("Session")
def __repr__(self):
return f"<WorkflowEvent(id={self.id}, type={self.event_type})>"
class LearningEvent(Base):
"""Track learning sessions and knowledge acquisition."""
__tablename__ = "learning_events"
id = Column(Integer, primary_key=True, index=True)
session_id = Column(String, ForeignKey("sessions.id"), nullable=False, index=True)
event_type = Column(String(50), nullable=False, index=True) # tutorial, documentation, experimentation
topic = Column(String(200), nullable=True, index=True) # What was being learned
resource_url = Column(Text, nullable=True) # Documentation URL, tutorial link
confidence_before = Column(Integer, nullable=True) # 1-10 scale
confidence_after = Column(Integer, nullable=True) # 1-10 scale
notes = Column(Text, nullable=True)
duration_ms = Column(Integer, nullable=True)
timestamp = Column(DateTime, nullable=False, default=datetime.utcnow, index=True)
session = relationship("Session")
def __repr__(self):
return f"<LearningEvent(id={self.id}, topic={self.topic})>"
class EnvironmentEvent(Base):
"""Track environment and configuration changes."""
__tablename__ = "environment_events"
id = Column(Integer, primary_key=True, index=True)
session_id = Column(String, ForeignKey("sessions.id"), nullable=False, index=True)
event_type = Column(String(50), nullable=False, index=True) # env_change, config_update, security_scan
environment = Column(String(50), nullable=True, index=True) # dev, staging, prod
config_file = Column(Text, nullable=True)
changes = Column(JSON, nullable=True) # What changed
impact_level = Column(String(20), nullable=True) # low, medium, high, critical
timestamp = Column(DateTime, nullable=False, default=datetime.utcnow, index=True)
session = relationship("Session")
def __repr__(self):
return f"<EnvironmentEvent(id={self.id}, type={self.event_type}, env={self.environment})>"
class CollaborationEvent(Base):
"""Track collaboration and external interactions."""
__tablename__ = "collaboration_events"
id = Column(Integer, primary_key=True, index=True)
session_id = Column(String, ForeignKey("sessions.id"), nullable=False, index=True)
event_type = Column(String(50), nullable=False, index=True) # external_resource, ai_question, review_request
interaction_type = Column(String(50), nullable=True) # documentation, stackoverflow, api_docs
query_or_topic = Column(Text, nullable=True)
resource_url = Column(Text, nullable=True)
response_quality = Column(Integer, nullable=True) # 1-5 rating
time_to_resolution = Column(Integer, nullable=True) # minutes
event_metadata = Column(JSON, nullable=True)
timestamp = Column(DateTime, nullable=False, default=datetime.utcnow, index=True)
session = relationship("Session")
def __repr__(self):
return f"<CollaborationEvent(id={self.id}, type={self.event_type})>"
class ProjectIntelligence(Base):
"""Track high-level project development patterns."""
__tablename__ = "project_intelligence"
id = Column(Integer, primary_key=True, index=True)
session_id = Column(String, ForeignKey("sessions.id"), nullable=False, index=True)
event_type = Column(String(50), nullable=False, index=True) # refactor, feature_flag, debugging_session
scope = Column(String(20), nullable=True, index=True) # small, medium, large
complexity = Column(String(20), nullable=True, index=True) # low, medium, high
start_time = Column(DateTime, nullable=False, default=datetime.utcnow)
end_time = Column(DateTime, nullable=True)
duration_minutes = Column(Integer, nullable=True)
files_affected = Column(JSON, nullable=True) # List of file paths
outcome = Column(String(20), nullable=True) # success, partial, failed, abandoned
notes = Column(Text, nullable=True)
timestamp = Column(DateTime, nullable=False, default=datetime.utcnow, index=True)
session = relationship("Session")
def __repr__(self):
return f"<ProjectIntelligence(id={self.id}, type={self.event_type}, scope={self.scope})>"
# Add hook_events relationship to Session model
def add_hook_relationships():
"""Add hook event relationships to the Session model."""
from app.models.session import Session
Session.hook_events = relationship("HookEvent", back_populates="session", cascade="all, delete-orphan")
Session.tool_errors = relationship("ToolError", cascade="all, delete-orphan")
Session.waiting_periods_new = relationship("WaitingPeriodNew", cascade="all, delete-orphan")
Session.performance_metrics = relationship("PerformanceMetric", cascade="all, delete-orphan")
Session.code_quality_events = relationship("CodeQualityEvent", cascade="all, delete-orphan")
Session.workflow_events = relationship("WorkflowEvent", cascade="all, delete-orphan")
Session.learning_events = relationship("LearningEvent", cascade="all, delete-orphan")
Session.environment_events = relationship("EnvironmentEvent", cascade="all, delete-orphan")
Session.collaboration_events = relationship("CollaborationEvent", cascade="all, delete-orphan")
Session.project_intelligence = relationship("ProjectIntelligence", cascade="all, delete-orphan")