Coverage for src / osiris_cli / progress_display.py: 0%
59 statements
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-27 17:41 +0200
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-27 17:41 +0200
1#!/usr/bin/env python3
2"""
3Progress Display - Beautiful progress bars for long operations
4"""
6import time
7from rich.console import Console
8from rich.progress import (
9 Progress,
10 SpinnerColumn,
11 TextColumn,
12 BarColumn,
13 TaskProgressColumn,
14 TimeRemainingColumn,
15 TimeElapsedColumn
16)
17from rich.live import Live
18from rich.text import Text
20console = Console()
23class ProgressTracker:
24 """
25 Beautiful progress tracking for long operations
26 """
28 def __init__(self):
29 self.progress = Progress(
30 SpinnerColumn(spinner_name="dots"),
31 TextColumn("[bold cyan]{task.description}"),
32 BarColumn(complete_style="green", finished_style="bold green"),
33 TaskProgressColumn(),
34 TimeElapsedColumn(),
35 TimeRemainingColumn(),
36 console=console,
37 transient=True
38 )
39 self.tasks = {}
41 def add_task(self, description: str, total: int = 100) -> int:
42 """Add a new task to track"""
43 task_id = self.progress.add_task(description, total=total)
44 self.tasks[description] = task_id
45 return task_id
47 def update(self, task_id: int, advance: int = 1, description: str = None):
48 """Update task progress"""
49 kwargs = {"advance": advance}
50 if description:
51 kwargs["description"] = description
52 self.progress.update(task_id, **kwargs)
54 def complete(self, task_id: int):
55 """Mark task as complete"""
56 self.progress.update(task_id, completed=True)
58 def __enter__(self):
59 """Context manager entry"""
60 self.progress.start()
61 return self
63 def __exit__(self, exc_type, exc_val, exc_tb):
64 """Context manager exit"""
65 self.progress.stop()
68class SimpleSpinner:
69 """
70 Simple spinner for indeterminate operations
71 """
73 def __init__(self, message: str = "Processing..."):
74 self.message = message
75 self.progress = Progress(
76 SpinnerColumn(spinner_name="dots"),
77 TextColumn("[bold cyan]{task.description}"),
78 console=console,
79 transient=True
80 )
81 self.task_id = None
83 def start(self):
84 """Start the spinner"""
85 self.progress.start()
86 self.task_id = self.progress.add_task(self.message)
88 def update(self, message: str):
89 """Update spinner message"""
90 if self.task_id is not None:
91 self.progress.update(self.task_id, description=message)
93 def stop(self, final_message: str = None):
94 """Stop the spinner"""
95 if final_message:
96 console.print(f"[green]✓[/green] {final_message}")
97 self.progress.stop()
99 def __enter__(self):
100 """Context manager entry"""
101 self.start()
102 return self
104 def __exit__(self, exc_type, exc_val, exc_tb):
105 """Context manager exit"""
106 self.stop()
109def show_progress(description: str, total: int, items: list):
110 """
111 Show progress for a list of items
113 Example:
114 show_progress("Processing files", 10, files)
115 """
116 with Progress(
117 SpinnerColumn(),
118 TextColumn("[bold cyan]{task.description}"),
119 BarColumn(),
120 TaskProgressColumn(),
121 TimeElapsedColumn(),
122 console=console
123 ) as progress:
124 task = progress.add_task(description, total=total)
126 for item in items:
127 # Process item
128 yield item
129 progress.advance(task)
132def with_spinner(message: str):
133 """
134 Decorator to show a spinner during function execution
136 Example:
137 @with_spinner("Loading data...")
138 def load_data():
139 # ... long operation
140 pass
141 """
142 def decorator(func):
143 def wrapper(*args, **kwargs):
144 with SimpleSpinner(message):
145 return func(*args, **kwargs)
146 return wrapper
147 return decorator