187 lines
5.1 KiB
Python
187 lines
5.1 KiB
Python
"""Visualize command for CLI."""
|
|
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
import click
|
|
from rich.console import Console
|
|
from rich.panel import Panel
|
|
from rich.text import Text
|
|
|
|
from ..core.database import SessionDatabase
|
|
from ..core.session import Session
|
|
from ..parsers.git_parser import GitLogParser
|
|
from ..parsers.pattern_analyzer import PatternDetector, SequenceAnalyzer
|
|
from ..exporters.ascii_generator import ASCIIGenerator, GitGraphGenerator
|
|
from ..exporters.mermaid_generator import MermaidGenerator
|
|
|
|
console = Console()
|
|
|
|
|
|
@click.command(name="visualize")
|
|
@click.argument("source", type=str)
|
|
@click.option(
|
|
"--session-id",
|
|
"-s",
|
|
type=int,
|
|
help="Session ID to visualize",
|
|
)
|
|
@click.option(
|
|
"--style",
|
|
"visual_style",
|
|
type=click.Choice(["compact", "detailed", "minimal"]),
|
|
default="detailed",
|
|
help="Visualization style",
|
|
)
|
|
@click.option(
|
|
"--format",
|
|
"-f",
|
|
type=click.Choice(["ascii", "mermaid"]),
|
|
default="ascii",
|
|
help="Output format",
|
|
)
|
|
@click.option(
|
|
"--output",
|
|
"-o",
|
|
type=click.Path(path_type=Path),
|
|
help="Output file",
|
|
)
|
|
@click.option(
|
|
"--analyze/--no-analyze",
|
|
default=True,
|
|
help="Include pattern analysis",
|
|
)
|
|
@click.pass_context
|
|
def visualize_cmd(
|
|
ctx: click.Context,
|
|
source: str,
|
|
session_id: Optional[int],
|
|
visual_style: str,
|
|
format: str,
|
|
output: Optional[Path],
|
|
analyze: bool,
|
|
) -> None:
|
|
"""Visualize a session or git workflow."""
|
|
db_path = ctx.obj.get("db", Path.home() / ".termflow" / "sessions.db")
|
|
|
|
if source == "session":
|
|
_visualize_session(
|
|
db_path,
|
|
session_id,
|
|
visual_style,
|
|
format,
|
|
output,
|
|
analyze,
|
|
)
|
|
elif source == "git":
|
|
_visualize_git(
|
|
Path.cwd(),
|
|
visual_style,
|
|
format,
|
|
output,
|
|
)
|
|
else:
|
|
console.print(f"[red]Unknown source: {source}[/red]")
|
|
console.print("Use 'session' or 'git' as source")
|
|
|
|
|
|
def _visualize_session(
|
|
db_path: Path,
|
|
session_id: Optional[int],
|
|
style: str,
|
|
format: str,
|
|
output: Optional[Path],
|
|
analyze: bool,
|
|
) -> None:
|
|
"""Visualize a terminal session."""
|
|
db = SessionDatabase(db_path)
|
|
|
|
if session_id is None:
|
|
sessions = db.get_all_sessions()
|
|
if not sessions:
|
|
console.print("[yellow]No sessions found[/yellow]")
|
|
return
|
|
session_id = sessions[0]["id"]
|
|
|
|
session = db.get_session(session_id)
|
|
if not session:
|
|
console.print(f"[red]Session {session_id} not found[/red]")
|
|
return
|
|
|
|
commands = db.get_session_commands(session_id)
|
|
command_list = [cmd.get("command", "") for cmd in commands]
|
|
|
|
if analyze:
|
|
detector = PatternDetector()
|
|
patterns = detector.analyze_commands(command_list)
|
|
|
|
if patterns:
|
|
text = Text()
|
|
text.append(f"Found {len(patterns)} patterns:\n", style="bold")
|
|
for i, pattern in enumerate(patterns[:5], 1):
|
|
text.append(f" {i}. {pattern.commands} (x{pattern.frequency})\n")
|
|
console.print(Panel(text, title="Pattern Analysis"))
|
|
|
|
metadata = {
|
|
"title": f"Session: {session.get('name', 'Unknown')}",
|
|
"command_count": len(command_list),
|
|
"session_name": session.get("name", ""),
|
|
}
|
|
|
|
if format == "ascii":
|
|
ascii_gen = ASCIIGenerator(style=style)
|
|
result = ascii_gen.generate_from_commands(command_list, metadata)
|
|
else:
|
|
mermaid_gen = MermaidGenerator()
|
|
result = mermaid_gen.generate_flowchart(
|
|
command_list,
|
|
title=f"Session: {session.get('name', 'Unknown')}",
|
|
metadata=metadata,
|
|
)
|
|
|
|
if output:
|
|
with open(output, "w") as f:
|
|
f.write(result)
|
|
console.print(f"[cyan]Visualization saved to {output}[/cyan]")
|
|
else:
|
|
console.print(Panel(result, title=f"Session Flow: {session.get('name', 'Unknown')}"))
|
|
|
|
|
|
def _visualize_git(
|
|
repo_path: Path,
|
|
style: str,
|
|
format: str,
|
|
output: Optional[Path],
|
|
) -> None:
|
|
"""Visualize git workflow."""
|
|
parser = GitLogParser(repo_path)
|
|
|
|
if not parser.is_git_repo():
|
|
console.print("[red]Not a git repository[/red]")
|
|
return
|
|
|
|
commits = parser.parse_log(max_count=50)
|
|
parser.get_commit_parents()
|
|
workflow = parser.analyze_workflow()
|
|
|
|
if format == "ascii":
|
|
graph_gen = GitGraphGenerator()
|
|
result = graph_gen.generate_from_parser(parser)
|
|
else:
|
|
mermaid_gen = MermaidGenerator()
|
|
result = mermaid_gen.generate_git_graph_from_parser(parser)
|
|
|
|
stats_text = Text()
|
|
stats_text.append(f"Commits: {len(commits)}\n", style="bold")
|
|
stats_text.append(f"Merges: {workflow.merge_count}\n")
|
|
stats_text.append(f"Features: {workflow.feature_count}\n")
|
|
stats_text.append(f"Branches: {len(workflow.branches)}")
|
|
|
|
if output:
|
|
with open(output, "w") as f:
|
|
f.write(result)
|
|
console.print(f"[cyan]Visualization saved to {output}[/cyan]")
|
|
else:
|
|
console.print(Panel(result, title="Git Workflow"))
|
|
console.print(Panel(stats_text, title="Statistics"))
|