From aa094bfb34911ba54ed2c62f2ec4b8fc1fd7b836 Mon Sep 17 00:00:00 2001 From: phernandez Date: Sun, 2 Feb 2025 21:53:25 -0600 Subject: [PATCH] add live console status to sync watch --- src/basic_memory/sync/watch_service.py | 109 +++++++++++++++++-------- 1 file changed, 76 insertions(+), 33 deletions(-) diff --git a/src/basic_memory/sync/watch_service.py b/src/basic_memory/sync/watch_service.py index fd84c741..643e4b0f 100644 --- a/src/basic_memory/sync/watch_service.py +++ b/src/basic_memory/sync/watch_service.py @@ -8,7 +8,10 @@ from datetime import datetime from pathlib import Path from typing import List, Optional +from rich import box from rich.console import Console +from rich.live import Live +from rich.table import Table from watchfiles import awatch, Change import os @@ -16,8 +19,6 @@ from basic_memory.config import ProjectConfig from basic_memory.sync.sync_service import SyncService from basic_memory.services.file_service import FileService -console = Console() - class WatchEvent(BaseModel): timestamp: datetime @@ -79,6 +80,56 @@ class WatchService: self.state = WatchServiceState() self.status_path = config.home / ".basic-memory" / "watch-status.json" self.status_path.parent.mkdir(parents=True, exist_ok=True) + self.console = Console() + + def generate_table(self) -> Table: + """Generate status display table""" + table = Table(title="Basic Memory Sync Status") + + # Add status row + table.add_column("Status", style="cyan") + table.add_column("Last Scan", style="cyan") + table.add_column("Files", style="cyan") + table.add_column("Errors", style="red") + + # Add main status row + table.add_row( + "✓ Running" if self.state.running else "✗ Stopped", + self.state.last_scan.strftime("%H:%M:%S") if self.state.last_scan else "-", + str(self.state.synced_files), + f"{self.state.error_count} ({self.state.last_error.strftime('%H:%M:%S') if self.state.last_error else 'none'})", + ) + + if self.state.recent_events: + # Add recent events + table.add_section() + table.add_row("Recent Events", "", "", "") + + for event in self.state.recent_events[:5]: # Show last 5 events + color = { + "new": "green", + "modified": "yellow", + "moved": "blue", + "deleted": "red", + "error": "red", + }.get(event.action, "white") + + icon = { + "new": "✚", + "modified": "✎", + "moved": "→", + "deleted": "✖", + "error": "!", + }.get(event.action, "*") + + table.add_row( + f"[{color}]{icon} {event.action}[/{color}]", + event.timestamp.strftime("%H:%M:%S"), + f"[{color}]{event.path}[/{color}]", + f"[dim]{event.checksum[:8] if event.checksum else ''}[/dim]", + ) + + return table async def run(self): """Watch for file changes and sync them""" @@ -86,24 +137,26 @@ class WatchService: self.state.start_time = datetime.now() await self.write_status() - console.print("\n[cyan]Watching for changes...[/cyan]") - try: - async for changes in awatch( - self.config.home, - watch_filter=self.filter_changes, - debounce=self.config.sync_delay, - recursive=True, - ): - # just sync the whole dir - await self.handle_changes(self.config.home) + with Live(self.generate_table(), refresh_per_second=4, console=self.console) as live: + try: + async for changes in awatch( + self.config.home, + watch_filter=self.filter_changes, + debounce=self.config.sync_delay, + recursive=True, + ): + # Process changes + await self.handle_changes(self.config.home) + # Update display + live.update(self.generate_table()) - except Exception as e: - self.state.record_error(str(e)) - await self.write_status() - raise - finally: - self.state.running = False - await self.write_status() + except Exception as e: + self.state.record_error(str(e)) + await self.write_status() + raise + finally: + self.state.running = False + await self.write_status() async def write_status(self): """Write current state to status file""" @@ -124,31 +177,21 @@ class WatchService: # Update stats for path in report.new: - event = self.state.add_event( + self.state.add_event( path=path, action="new", status="success", checksum=report.checksums[path] ) - console.print( - f"{event.timestamp.isoformat(timespec='minutes')} New:\t\t [green]{path}[/green] ({event.checksum[:8]})" - ) for path in report.modified: - event = self.state.add_event( + self.state.add_event( path=path, action="modified", status="success", checksum=report.checksums[path] ) - console.print( - f"{event.timestamp.isoformat(timespec='minutes')} Modified:\t [yellow]{path}[/yellow] ({event.checksum[:8]})" - ) for old_path, new_path in report.moves.items(): - event = self.state.add_event( + self.state.add_event( path=f"{old_path} -> {new_path}", action="moved", status="success", checksum=report.checksums[new_path], ) - console.print( - f"{event.timestamp.isoformat(timespec='minutes')} Moved:\t\t [blue]{old_path} -> {new_path}[/blue] ({event.checksum[:8]})" - ) for path in report.deleted: - event = self.state.add_event(path=path, action="deleted", status="success") - console.print(f"{event.timestamp.isoformat(timespec='minutes')} Deleted:\t [red]{path}[/red]") + self.state.add_event(path=path, action="deleted", status="success") await self.write_status()