fix: Prevent TUI corruption from logging and improve sync feedback #1
3
.gitignore
vendored
3
.gitignore
vendored
@ -13,3 +13,6 @@ wheels/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
.venv/
|
||||
|
||||
# Data directory (contains user settings, logs, and database)
|
||||
data/
|
||||
|
||||
63
TROUBLESHOOTING.md
Normal file
63
TROUBLESHOOTING.md
Normal file
@ -0,0 +1,63 @@
|
||||
# Troubleshooting Guide
|
||||
|
||||
## Sync Errors
|
||||
|
||||
### API Key Configuration
|
||||
|
||||
The app requires a valid API key to sync data from the SBA Major Domo API.
|
||||
|
||||
**Option 1: Settings Screen (Recommended)**
|
||||
1. Launch the app: `sba-scout`
|
||||
2. Press `x` to open Settings
|
||||
3. Enter your API key
|
||||
4. Click "Save Settings"
|
||||
|
||||
**Option 2: Edit settings.yaml directly**
|
||||
1. Edit `data/settings.yaml`
|
||||
2. Set `api.api_key` to your actual API key
|
||||
3. Save the file
|
||||
4. Restart the app
|
||||
|
||||
### Viewing Logs
|
||||
|
||||
All errors are now logged to `data/logs/sba_scout.log` instead of the terminal.
|
||||
|
||||
To view recent errors:
|
||||
```bash
|
||||
tail -f data/logs/sba_scout.log
|
||||
```
|
||||
|
||||
To search for specific errors:
|
||||
```bash
|
||||
grep "ERROR" data/logs/sba_scout.log
|
||||
```
|
||||
|
||||
### Common Errors
|
||||
|
||||
**"API key not configured"**
|
||||
- Your API key is missing or set to the placeholder value
|
||||
- Fix: Configure your API key using one of the methods above
|
||||
|
||||
**"Sync failed: Invalid API key"** (401/403 error)
|
||||
- Your API key is invalid or expired
|
||||
- Fix: Get a new API key from the SBA Major Domo system
|
||||
|
||||
**"Sync failed: API error 500"**
|
||||
- The API server is experiencing issues
|
||||
- Fix: Wait a few minutes and try again
|
||||
|
||||
**"Request failed: Connection timeout"**
|
||||
- Network connectivity issues
|
||||
- Fix: Check your internet connection, or increase timeout in settings
|
||||
|
||||
## Getting an API Key
|
||||
|
||||
To get an API key for the SBA Major Domo API:
|
||||
1. Visit the SBA Major Domo system
|
||||
2. Go to your account settings
|
||||
3. Generate a new API key
|
||||
4. Copy the key and paste it into the app settings
|
||||
|
||||
## Need More Help?
|
||||
|
||||
Check the full logs at `data/logs/sba_scout.log` for detailed error messages and stack traces.
|
||||
@ -112,11 +112,16 @@ class LeagueAPIClient:
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except httpx.HTTPStatusError as e:
|
||||
logger.error(f"API error: {e.response.status_code} - {e.response.text}")
|
||||
raise LeagueAPIError(
|
||||
f"API request failed: {e.response.text}",
|
||||
status_code=e.response.status_code,
|
||||
)
|
||||
# Check if this is a Cloudflare HTML error page
|
||||
response_text = e.response.text
|
||||
if "cloudflare" in response_text.lower() and e.response.status_code == 403:
|
||||
error_msg = "API blocked by Cloudflare (missing or invalid API key)"
|
||||
logger.error(f"API error: {e.response.status_code} - Cloudflare block")
|
||||
else:
|
||||
error_msg = f"API request failed (HTTP {e.response.status_code})"
|
||||
logger.error(f"API error: {e.response.status_code} - {response_text[:200]}")
|
||||
|
||||
raise LeagueAPIError(error_msg, status_code=e.response.status_code)
|
||||
except httpx.RequestError as e:
|
||||
logger.error(f"Request error: {e}")
|
||||
raise LeagueAPIError(f"Request failed: {str(e)}")
|
||||
|
||||
@ -23,10 +23,20 @@ from .screens.matchup import MatchupScreen
|
||||
from .screens.roster import RosterScreen
|
||||
from .screens.settings import SettingsScreen
|
||||
|
||||
# Configure logging
|
||||
# Configure logging - write to file to avoid interfering with TUI
|
||||
from pathlib import Path
|
||||
|
||||
log_dir = Path("data/logs")
|
||||
log_dir.mkdir(parents=True, exist_ok=True)
|
||||
log_file = log_dir / "sba_scout.log"
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
||||
handlers=[
|
||||
logging.FileHandler(log_file),
|
||||
# Don't add StreamHandler - it corrupts the TUI
|
||||
],
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -100,6 +110,7 @@ class DashboardScreen(Screen):
|
||||
# Status bar
|
||||
with Horizontal(id="status-bar"):
|
||||
yield Label("Last sync: Never", id="sync-status")
|
||||
yield LoadingIndicator(id="sync-loader")
|
||||
yield Button("Sync Now [s]", id="btn-sync", variant="success")
|
||||
yield Button("Settings [x]", id="btn-settings", variant="default")
|
||||
|
||||
@ -186,28 +197,47 @@ class DashboardScreen(Screen):
|
||||
|
||||
async def action_sync_data(self) -> None:
|
||||
"""Sync data from the league API."""
|
||||
from .api.client import LeagueAPIError
|
||||
from .api.sync import sync_all
|
||||
|
||||
sync_btn = self.query_one("#btn-sync", Button)
|
||||
sync_status = self.query_one("#sync-status", Label)
|
||||
sync_loader = self.query_one("#sync-loader", LoadingIndicator)
|
||||
|
||||
sync_btn.disabled = True
|
||||
sync_loader.display = True
|
||||
sync_status.update("Syncing...")
|
||||
|
||||
try:
|
||||
settings = get_settings()
|
||||
|
||||
async with get_session() as session:
|
||||
counts = await sync_all(session, season=13)
|
||||
counts = await sync_all(session, season=settings.team.current_season)
|
||||
|
||||
sync_status.update(f"Synced: {counts['teams']} teams, {counts['players']} players")
|
||||
|
||||
# Refresh roster summary
|
||||
await self.load_roster_summary()
|
||||
|
||||
except LeagueAPIError as e:
|
||||
logger.error(f"API error during sync: {e.message}")
|
||||
if "cloudflare" in e.message.lower() or e.status_code == 403:
|
||||
sync_status.update("Sync disabled: API key required")
|
||||
elif e.status_code == 401:
|
||||
sync_status.update("Sync failed: Invalid API key")
|
||||
elif e.status_code:
|
||||
sync_status.update(f"Sync failed: HTTP {e.status_code}")
|
||||
else:
|
||||
sync_status.update(f"Sync failed: {str(e.message)[:40]}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Sync failed: {e}")
|
||||
sync_status.update(f"Sync failed: {str(e)[:50]}")
|
||||
logger.error(f"Unexpected error during sync: {e}", exc_info=True)
|
||||
# Show a user-friendly truncated message
|
||||
error_msg = str(e).split("\n")[0][:50]
|
||||
sync_status.update(f"Sync failed: {error_msg}")
|
||||
|
||||
finally:
|
||||
sync_loader.display = False
|
||||
sync_btn.disabled = False
|
||||
|
||||
|
||||
@ -313,6 +343,12 @@ class SBAScoutApp(App):
|
||||
width: 1fr;
|
||||
}
|
||||
|
||||
#sync-loader {
|
||||
width: auto;
|
||||
margin-right: 1;
|
||||
display: none;
|
||||
}
|
||||
|
||||
#btn-sync {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user