This commit is contained in:
170
src/storage/models.py
Normal file
170
src/storage/models.py
Normal file
@@ -0,0 +1,170 @@
|
||||
"""Data models for DevTrace."""
|
||||
|
||||
from datetime import datetime
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Optional, Dict, Any
|
||||
|
||||
|
||||
@dataclass
|
||||
class Session:
|
||||
"""Represents a development session."""
|
||||
id: int
|
||||
name: str
|
||||
directory: str
|
||||
start_time: Optional[datetime] = None
|
||||
end_time: Optional[datetime] = None
|
||||
notes: Optional[str] = None
|
||||
|
||||
@property
|
||||
def duration(self) -> Optional[float]:
|
||||
"""Calculate session duration in seconds."""
|
||||
if self.start_time and self.end_time:
|
||||
return (self.end_time - self.start_time).total_seconds()
|
||||
return None
|
||||
|
||||
|
||||
@dataclass
|
||||
class FileEvent:
|
||||
"""Represents a file system event."""
|
||||
id: int
|
||||
session_id: int
|
||||
event_type: str
|
||||
file_path: str
|
||||
timestamp: Optional[datetime] = None
|
||||
details: Optional[str] = None
|
||||
content_hash: Optional[str] = None
|
||||
|
||||
@property
|
||||
def filename(self) -> str:
|
||||
"""Get just the filename from the path."""
|
||||
return self.file_path.split("/")[-1]
|
||||
|
||||
@property
|
||||
def extension(self) -> str:
|
||||
"""Get the file extension."""
|
||||
return self.filename.split(".")[-1] if "." in self.filename else ""
|
||||
|
||||
|
||||
@dataclass
|
||||
class CommandEvent:
|
||||
"""Represents a shell command execution."""
|
||||
id: int
|
||||
session_id: int
|
||||
command: str
|
||||
timestamp: Optional[datetime] = None
|
||||
exit_code: Optional[int] = None
|
||||
working_directory: Optional[str] = None
|
||||
details: Optional[str] = None
|
||||
|
||||
@property
|
||||
def was_successful(self) -> Optional[bool]:
|
||||
"""Check if command was successful."""
|
||||
if self.exit_code is None:
|
||||
return None
|
||||
return self.exit_code == 0
|
||||
|
||||
@property
|
||||
def short_command(self) -> str:
|
||||
"""Get truncated command for display."""
|
||||
max_len = 50
|
||||
if len(self.command) <= max_len:
|
||||
return self.command
|
||||
return self.command[:max_len] + "..."
|
||||
|
||||
|
||||
@dataclass
|
||||
class GitEvent:
|
||||
"""Represents a git operation."""
|
||||
id: int
|
||||
session_id: int
|
||||
operation: str
|
||||
branch: Optional[str] = None
|
||||
commit_hash: Optional[str] = None
|
||||
timestamp: Optional[datetime] = None
|
||||
details: Optional[str] = None
|
||||
diff: Optional[str] = None
|
||||
|
||||
@property
|
||||
def short_hash(self) -> str:
|
||||
"""Get short commit hash."""
|
||||
if self.commit_hash:
|
||||
return self.commit_hash[:7]
|
||||
return ""
|
||||
|
||||
|
||||
@dataclass
|
||||
class SearchResult:
|
||||
"""Represents a search result."""
|
||||
event_type: str
|
||||
event_id: int
|
||||
score: float
|
||||
details: Dict[str, Any]
|
||||
content: str = ""
|
||||
|
||||
|
||||
@dataclass
|
||||
class TimelineEvent:
|
||||
"""Unified event representation for timeline display."""
|
||||
id: int
|
||||
session_id: int
|
||||
event_type: str
|
||||
timestamp: Optional[datetime]
|
||||
details: Dict[str, Any]
|
||||
display_text: str
|
||||
icon: str
|
||||
|
||||
@classmethod
|
||||
def from_file_event(cls, event: FileEvent) -> "TimelineEvent":
|
||||
"""Create TimelineEvent from FileEvent."""
|
||||
icons = {
|
||||
"created": "📄",
|
||||
"modified": "✏️",
|
||||
"deleted": "🗑️",
|
||||
"moved": "📁"
|
||||
}
|
||||
return cls(
|
||||
id=event.id,
|
||||
session_id=event.session_id,
|
||||
event_type="file",
|
||||
timestamp=event.timestamp,
|
||||
details={"file_path": event.file_path, "content_hash": event.content_hash},
|
||||
display_text=f"{event.event_type} {event.filename}",
|
||||
icon=icons.get(event.event_type, "📄")
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_command_event(cls, event: CommandEvent) -> "TimelineEvent":
|
||||
"""Create TimelineEvent from CommandEvent."""
|
||||
status = "✅" if event.was_successful else "❌" if event.was_successful is False else "⏳"
|
||||
return cls(
|
||||
id=event.id,
|
||||
session_id=event.session_id,
|
||||
event_type="command",
|
||||
timestamp=event.timestamp,
|
||||
details={"command": event.command, "exit_code": event.exit_code},
|
||||
display_text=f"{event.short_command}",
|
||||
icon=f"⚡ {status}"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_git_event(cls, event: GitEvent) -> "TimelineEvent":
|
||||
"""Create TimelineEvent from GitEvent."""
|
||||
icons = {
|
||||
"commit": "💾",
|
||||
"branch": "🌿",
|
||||
"merge": "🔀",
|
||||
"checkout": "🔀",
|
||||
"push": "📤",
|
||||
"pull": "📥",
|
||||
"rebase": "📜",
|
||||
"reset": "⏪"
|
||||
}
|
||||
return cls(
|
||||
id=event.id,
|
||||
session_id=event.session_id,
|
||||
event_type="git",
|
||||
timestamp=event.timestamp,
|
||||
details={"operation": event.operation, "branch": event.branch, "commit": event.commit_hash},
|
||||
display_text=f"{event.operation} {event.branch or event.short_hash}",
|
||||
icon=icons.get(event.operation, "🔧")
|
||||
)
|
||||
Reference in New Issue
Block a user