mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-04-10 21:03:01 +00:00
feat: add error detection, monitoring, and code quality improvements (#223)
* feat: add error detection, monitoring, and code quality improvements This comprehensive update addresses automatic error detection, code quality, and plugin development experience: ## Error Detection & Monitoring - Add ErrorAggregator service for centralized error tracking - Add pattern detection for recurring errors (5+ in 60 min) - Add error dashboard API endpoints (/api/v3/errors/*) - Integrate error recording into plugin executor ## Code Quality - Remove 10 silent `except: pass` blocks in sports.py and football.py - Remove hardcoded debug log paths - Add pre-commit hooks to prevent future bare except clauses ## Validation & Type Safety - Add warnings when plugins lack config_schema.json - Add config key collision detection for plugins - Improve type coercion logging in BasePlugin ## Testing - Add test_config_validation_edge_cases.py - Add test_plugin_loading_failures.py - Add test_error_aggregator.py ## Documentation - Add PLUGIN_ERROR_HANDLING.md guide - Add CONFIG_DEBUGGING.md guide Note: GitHub Actions CI workflow is available in the plan but requires workflow scope to push. Add .github/workflows/ci.yml manually. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: address code review issues - Fix GitHub issues URL in CONFIG_DEBUGGING.md - Use RLock in error_aggregator.py to prevent deadlock in export_to_file - Distinguish missing vs invalid schema files in plugin_manager.py - Add assertions to test_null_value_for_required_field test - Remove unused initial_count variable in test_plugin_load_error_recorded - Add validation for max_age_hours in clear_old_errors API endpoint Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Chuck <chuck@example.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -207,25 +207,6 @@ class SportsCore(ABC):
|
||||
|
||||
def display(self, force_clear: bool = False) -> bool:
|
||||
"""Common display method for all NCAA FB managers""" # Updated docstring
|
||||
# #region agent log
|
||||
import json
|
||||
try:
|
||||
with open('/home/chuck/Github/LEDMatrix/.cursor/debug.log', 'a') as f:
|
||||
f.write(json.dumps({
|
||||
"sessionId": "debug-session",
|
||||
"runId": "run1",
|
||||
"hypothesisId": "D",
|
||||
"location": "sports.py:208",
|
||||
"message": "Display called",
|
||||
"data": {
|
||||
"force_clear": force_clear,
|
||||
"has_current_game": self.current_game is not None,
|
||||
"current_game": self.current_game['away_abbr'] + "@" + self.current_game['home_abbr'] if self.current_game else None
|
||||
},
|
||||
"timestamp": int(time.time() * 1000)
|
||||
}) + "\n")
|
||||
except: pass
|
||||
# #endregion
|
||||
if not self.is_enabled: # Check if module is enabled
|
||||
return False
|
||||
|
||||
@@ -248,40 +229,7 @@ class SportsCore(ABC):
|
||||
return False
|
||||
|
||||
try:
|
||||
# #region agent log
|
||||
try:
|
||||
with open('/home/chuck/Github/LEDMatrix/.cursor/debug.log', 'a') as f:
|
||||
f.write(json.dumps({
|
||||
"sessionId": "debug-session",
|
||||
"runId": "run1",
|
||||
"hypothesisId": "D",
|
||||
"location": "sports.py:232",
|
||||
"message": "About to draw scorebug",
|
||||
"data": {
|
||||
"force_clear": force_clear,
|
||||
"game": self.current_game['away_abbr'] + "@" + self.current_game['home_abbr'] if self.current_game else None
|
||||
},
|
||||
"timestamp": int(time.time() * 1000)
|
||||
}) + "\n")
|
||||
except: pass
|
||||
# #endregion
|
||||
self._draw_scorebug_layout(self.current_game, force_clear)
|
||||
# #region agent log
|
||||
try:
|
||||
with open('/home/chuck/Github/LEDMatrix/.cursor/debug.log', 'a') as f:
|
||||
f.write(json.dumps({
|
||||
"sessionId": "debug-session",
|
||||
"runId": "run1",
|
||||
"hypothesisId": "D",
|
||||
"location": "sports.py:235",
|
||||
"message": "After draw scorebug",
|
||||
"data": {
|
||||
"force_clear": force_clear
|
||||
},
|
||||
"timestamp": int(time.time() * 1000)
|
||||
}) + "\n")
|
||||
except: pass
|
||||
# #endregion
|
||||
# display_manager.update_display() should be called within subclass draw methods
|
||||
# or after calling display() in the main loop. Let's keep it out of the base display.
|
||||
return True
|
||||
@@ -1443,48 +1391,9 @@ class SportsLive(SportsCore):
|
||||
self.live_games = sorted(new_live_games, key=lambda g: g.get('start_time_utc') or datetime.now(timezone.utc)) # Sort by start time
|
||||
# Reset index if current game is gone or list is new
|
||||
if not self.current_game or self.current_game['id'] not in new_game_ids:
|
||||
# #region agent log
|
||||
import json
|
||||
try:
|
||||
with open('/home/chuck/Github/LEDMatrix/.cursor/debug.log', 'a') as f:
|
||||
f.write(json.dumps({
|
||||
"sessionId": "debug-session",
|
||||
"runId": "run1",
|
||||
"hypothesisId": "B",
|
||||
"location": "sports.py:1393",
|
||||
"message": "Games loaded - resetting index and last_game_switch",
|
||||
"data": {
|
||||
"current_game_before": self.current_game['id'] if self.current_game else None,
|
||||
"live_games_count": len(self.live_games),
|
||||
"last_game_switch_before": self.last_game_switch,
|
||||
"current_time": current_time,
|
||||
"time_since_init": current_time - self.last_game_switch if self.last_game_switch > 0 else None
|
||||
},
|
||||
"timestamp": int(time.time() * 1000)
|
||||
}) + "\n")
|
||||
except: pass
|
||||
# #endregion
|
||||
self.current_game_index = 0
|
||||
self.current_game = self.live_games[0] if self.live_games else None
|
||||
self.last_game_switch = current_time
|
||||
# #region agent log
|
||||
try:
|
||||
with open('/home/chuck/Github/LEDMatrix/.cursor/debug.log', 'a') as f:
|
||||
f.write(json.dumps({
|
||||
"sessionId": "debug-session",
|
||||
"runId": "run1",
|
||||
"hypothesisId": "B",
|
||||
"location": "sports.py:1396",
|
||||
"message": "Games loaded - after setting last_game_switch",
|
||||
"data": {
|
||||
"current_game_after": self.current_game['id'] if self.current_game else None,
|
||||
"last_game_switch_after": self.last_game_switch,
|
||||
"first_game": self.current_game['away_abbr'] + "@" + self.current_game['home_abbr'] if self.current_game else None
|
||||
},
|
||||
"timestamp": int(time.time() * 1000)
|
||||
}) + "\n")
|
||||
except: pass
|
||||
# #endregion
|
||||
else:
|
||||
# Find current game's new index if it still exists
|
||||
try:
|
||||
@@ -1530,70 +1439,9 @@ class SportsLive(SportsCore):
|
||||
# Handle game switching (outside test mode check)
|
||||
# Fix: Don't check for switching if last_game_switch is still 0 (games haven't been loaded yet)
|
||||
# This prevents immediate switching when the system has been running for a while before games load
|
||||
# #region agent log
|
||||
import json
|
||||
try:
|
||||
with open('/home/chuck/Github/LEDMatrix/.cursor/debug.log', 'a') as f:
|
||||
f.write(json.dumps({
|
||||
"sessionId": "debug-session",
|
||||
"runId": "run1",
|
||||
"hypothesisId": "A",
|
||||
"location": "sports.py:1432",
|
||||
"message": "Game switch check - before condition",
|
||||
"data": {
|
||||
"test_mode": self.test_mode,
|
||||
"live_games_count": len(self.live_games),
|
||||
"current_time": current_time,
|
||||
"last_game_switch": self.last_game_switch,
|
||||
"time_since_switch": current_time - self.last_game_switch,
|
||||
"game_display_duration": self.game_display_duration,
|
||||
"current_game_index": self.current_game_index,
|
||||
"will_switch": not self.test_mode and len(self.live_games) > 1 and self.last_game_switch > 0 and (current_time - self.last_game_switch) >= self.game_display_duration
|
||||
},
|
||||
"timestamp": int(time.time() * 1000)
|
||||
}) + "\n")
|
||||
except: pass
|
||||
# #endregion
|
||||
if not self.test_mode and len(self.live_games) > 1 and self.last_game_switch > 0 and (current_time - self.last_game_switch) >= self.game_display_duration:
|
||||
# #region agent log
|
||||
try:
|
||||
with open('/home/chuck/Github/LEDMatrix/.cursor/debug.log', 'a') as f:
|
||||
f.write(json.dumps({
|
||||
"sessionId": "debug-session",
|
||||
"runId": "run1",
|
||||
"hypothesisId": "A",
|
||||
"location": "sports.py:1433",
|
||||
"message": "Game switch triggered",
|
||||
"data": {
|
||||
"old_index": self.current_game_index,
|
||||
"old_game": self.current_game['away_abbr'] + "@" + self.current_game['home_abbr'] if self.current_game else None,
|
||||
"time_since_switch": current_time - self.last_game_switch,
|
||||
"last_game_switch_before": self.last_game_switch
|
||||
},
|
||||
"timestamp": int(time.time() * 1000)
|
||||
}) + "\n")
|
||||
except: pass
|
||||
# #endregion
|
||||
self.current_game_index = (self.current_game_index + 1) % len(self.live_games)
|
||||
self.current_game = self.live_games[self.current_game_index]
|
||||
self.last_game_switch = current_time
|
||||
# #region agent log
|
||||
try:
|
||||
with open('/home/chuck/Github/LEDMatrix/.cursor/debug.log', 'a') as f:
|
||||
f.write(json.dumps({
|
||||
"sessionId": "debug-session",
|
||||
"runId": "run1",
|
||||
"hypothesisId": "A",
|
||||
"location": "sports.py:1436",
|
||||
"message": "Game switch completed",
|
||||
"data": {
|
||||
"new_index": self.current_game_index,
|
||||
"new_game": self.current_game['away_abbr'] + "@" + self.current_game['home_abbr'] if self.current_game else None,
|
||||
"last_game_switch_after": self.last_game_switch
|
||||
},
|
||||
"timestamp": int(time.time() * 1000)
|
||||
}) + "\n")
|
||||
except: pass
|
||||
# #endregion
|
||||
self.logger.info(f"Switched live view to: {self.current_game['away_abbr']}@{self.current_game['home_abbr']}") # Changed log prefix
|
||||
# Force display update via flag or direct call if needed, but usually let main loop handle
|
||||
|
||||
Reference in New Issue
Block a user