logging updates for all screens

This commit is contained in:
rambros 2026-03-04 15:56:55 +05:30
parent eb89a6c453
commit bb805c7cfe
4 changed files with 31 additions and 14 deletions

View file

@ -6,7 +6,7 @@ from pydantic import BaseModel, Field
class MigrationSettings(BaseModel): class MigrationSettings(BaseModel):
batch_size: int = Field(default=100) batch_size: int = Field(default=100)
rate_limit_delay_seconds: int = Field(default=2) rate_limit_delay_seconds: int = Field(default=2)
log_level: str = Field(default="INFO") log_level: str = Field(default="DEBUG")
class AppConfig(BaseModel): class AppConfig(BaseModel):
discord_bot_token: str discord_bot_token: str

View file

@ -126,7 +126,7 @@ class BackupPane(Container):
@work(exclusive=True) @work(exclusive=True)
async def run_backup_profile(self) -> None: async def run_backup_profile(self) -> None:
modal = ProgressScreen() modal = ProgressScreen(log_level=self.config.migration.log_level)
self.app.push_screen(modal) self.app.push_screen(modal)
await asyncio.sleep(0.1) await asyncio.sleep(0.1)
modal.phase_progress() modal.phase_progress()
@ -164,7 +164,7 @@ class BackupPane(Container):
@work(exclusive=True) @work(exclusive=True)
async def run_backup_messages(self) -> None: async def run_backup_messages(self) -> None:
modal_prog = ProgressScreen() modal_prog = ProgressScreen(log_level=self.config.migration.log_level)
self.app.push_screen(modal_prog) self.app.push_screen(modal_prog)
await asyncio.sleep(0.1) await asyncio.sleep(0.1)
@ -223,7 +223,7 @@ class BackupPane(Container):
selected_channels = [c for c in eligible_channels if c.id in selected_ids] selected_channels = [c for c in eligible_channels if c.id in selected_ids]
# Phase 2: Confirmation # Phase 2: Confirmation
modal_prog = ProgressScreen() # Re-instantiate to avoid Textual re-push UI freeze modal_prog = ProgressScreen(log_level=self.config.migration.log_level) # Re-instantiate to avoid Textual re-push UI freeze
self.app.push_screen(modal_prog) self.app.push_screen(modal_prog)
await asyncio.sleep(0.1) await asyncio.sleep(0.1)
@ -318,7 +318,7 @@ class BackupPane(Container):
@work(exclusive=True) @work(exclusive=True)
async def run_backup_sync(self) -> None: async def run_backup_sync(self) -> None:
modal_prog = ProgressScreen() modal_prog = ProgressScreen(log_level=self.config.migration.log_level)
self.app.push_screen(modal_prog) self.app.push_screen(modal_prog)
await asyncio.sleep(0.1) await asyncio.sleep(0.1)
modal_prog.phase_progress() modal_prog.phase_progress()

View file

@ -116,7 +116,9 @@ class ProgressScreen(Screen[None]):
yield Button("Cancel", id="btn_cancel", variant="error") yield Button("Cancel", id="btn_cancel", variant="error")
yield Footer() yield Footer()
def on_mount(self): def __init__(self, log_level: str = "INFO", *args, **kwargs):
super().__init__(*args, **kwargs)
self.log_level = log_level.upper()
self.confirm_future = None self.confirm_future = None
self.cancel_callback = None self.cancel_callback = None
self.start_time = time.time() self.start_time = time.time()
@ -132,10 +134,19 @@ class ProgressScreen(Screen[None]):
# Intercept Python logs and pipe to the #live_log # Intercept Python logs and pipe to the #live_log
self.log_handler = UILogHandler(self.write_live) self.log_handler = UILogHandler(self.write_live)
# Set level based on config
level = getattr(logging, self.log_level, logging.INFO)
# Attach to root logger # Attach to root logger
logging.getLogger().addHandler(self.log_handler) root_logger = logging.getLogger()
root_logger.addHandler(self.log_handler)
root_logger.setLevel(level)
# Also let's capture discord.py logs specifically if they aren't propagating # Also let's capture discord.py logs specifically if they aren't propagating
logging.getLogger("discord").addHandler(self.log_handler) discord_logger = logging.getLogger("discord")
discord_logger.addHandler(self.log_handler)
discord_logger.setLevel(level)
def on_unmount(self): def on_unmount(self):
# Detach log handler when UI is cleanly removed # Detach log handler when UI is cleanly removed
@ -513,8 +524,13 @@ class ChannelPickerScreen(Screen[tuple]):
def _render_pane(self, channels, categories, pane_id, prefix): def _render_pane(self, channels, categories, pane_id, prefix):
cat_grouped: dict[int | None, list] = {} cat_grouped: dict[int | None, list] = {}
seen_ids = set() # Prevent duplicate widget IDs
for c in channels: for c in channels:
cat_id = getattr(c, "category_id", None) if not isinstance(c, dict) else c.get("parent_id") cat_id = getattr(c, "category_id", None) if not isinstance(c, dict) else c.get("parent_id")
cid = c.get("id") if isinstance(c, dict) else c.id
if cid in seen_ids:
continue
seen_ids.add(cid)
cat_grouped.setdefault(cat_id, []).append(c) cat_grouped.setdefault(cat_id, []).append(c)
with VerticalScroll(classes="split_pane", id=pane_id): with VerticalScroll(classes="split_pane", id=pane_id):

View file

@ -142,7 +142,8 @@ class ShuttlePane(Container):
return f"Reaper-{self.cfg_name}" return f"Reaper-{self.cfg_name}"
def _rebuild_engine(self): def _rebuild_engine(self):
self.engine = MigrationContext(self.config, self.target_platform) source = "backup" if self.config.tool_mode == "backup_transfer" else "live"
self.engine = MigrationContext(self.config, self.target_platform, source_mode=source)
# ── labels ──────────────────────────────────────────────────────────── # ── labels ────────────────────────────────────────────────────────────
@ -339,7 +340,7 @@ class ShuttlePane(Container):
@work(exclusive=True) @work(exclusive=True)
async def run_batch_clone(self, selections: list[str]) -> None: async def run_batch_clone(self, selections: list[str]) -> None:
modal = ProgressScreen() modal = ProgressScreen(log_level=self.config.migration.log_level)
self.app.push_screen(modal) self.app.push_screen(modal)
await asyncio.sleep(0.1) await asyncio.sleep(0.1)
connections_started = False connections_started = False
@ -450,7 +451,7 @@ class ShuttlePane(Container):
@work(exclusive=True) @work(exclusive=True)
async def run_batch_sync(self, selections: list[str]) -> None: async def run_batch_sync(self, selections: list[str]) -> None:
modal = ProgressScreen() modal = ProgressScreen(log_level=self.config.migration.log_level)
self.app.push_screen(modal) self.app.push_screen(modal)
await asyncio.sleep(0.1) await asyncio.sleep(0.1)
try: try:
@ -680,7 +681,7 @@ class ShuttlePane(Container):
migrate_mod = fluxer_migrate if self.target_platform == "fluxer" else stoat_migrate migrate_mod = fluxer_migrate if self.target_platform == "fluxer" else stoat_migrate
platform_name = self.target_platform.capitalize() platform_name = self.target_platform.capitalize()
modal = ProgressScreen() modal = ProgressScreen(log_level=self.config.migration.log_level)
self.app.push_screen(modal) self.app.push_screen(modal)
await asyncio.sleep(0.1) await asyncio.sleep(0.1)
@ -738,7 +739,7 @@ class ShuttlePane(Container):
has_previous = bool(last_migrated) has_previous = bool(last_migrated)
# Analyze # Analyze
modal = ProgressScreen() modal = ProgressScreen(log_level=self.config.migration.log_level)
self.app.push_screen(modal) self.app.push_screen(modal)
await asyncio.sleep(0.1) await asyncio.sleep(0.1)
modal.set_status("Analyzing channel...") modal.set_status("Analyzing channel...")
@ -918,7 +919,7 @@ class ShuttlePane(Container):
@work(exclusive=True) @work(exclusive=True)
async def run_batch_danger(self, selections: list[str]) -> None: async def run_batch_danger(self, selections: list[str]) -> None:
modal = ProgressScreen() modal = ProgressScreen(log_level=self.config.migration.log_level)
self.app.push_screen(modal) self.app.push_screen(modal)
await asyncio.sleep(0.1) await asyncio.sleep(0.1)
target_started = False target_started = False