Skip to content

fix(soccer-scoreboard): let custom leagues (eng.2 etc.) save - #238

Merged
ChuckBuilds merged 2 commits into
mainfrom
claude/championship-football-config-2klztd
Jul 31, 2026
Merged

fix(soccer-scoreboard): let custom leagues (eng.2 etc.) save#238
ChuckBuilds merged 2 commits into
mainfrom
claude/championship-football-config-2klztd

Conversation

@ChuckBuilds

Copy link
Copy Markdown
Owner

Pull Request

Summary

Adding a league under Add More Leagues failed to save with HTTP 400 no matter which league code was entered, so the English Championship (eng.2) could not be configured at all. The custom_leagues schema now accepts the values the web UI's row editor actually submits, and the manager normalizes them back to the shapes it expects.

Type of change

  • Bug fix in an existing plugin
  • New plugin (also fill out the SUBMISSION.md checklist below)
  • New feature for an existing plugin
  • Documentation only
  • Repo-wide change (registry script, hook, top-level docs)

Plugin(s) affected

soccer-scoreboard

Related issues

N/A — reported directly.

Root cause

The array-table row editor keeps every non-column property of a row in a hidden text input. An empty input round-trips back through coerceValue() as null, and a list property comes back as the comma-separated string the user typed. custom_leagues declared those properties as strict "array" / "object" / "number", so a brand-new row submitted:

favorite_teams: null
exclude_teams: null
dynamic_duration.max_duration_seconds: null
dynamic_duration.modes: null

Draft-7 validation rejected all four and blocked the entire config save — the league code was never the problem.

Changes

  • config_schema.json — properties the row editor can leave empty now accept null; favorite_teams / exclude_teams also accept the comma-separated text the editor produces.
  • config_schema.json — the league_code pattern no longer rejects valid ESPN codes with underscores or prefixes longer than four letters (eng.league_cup, conmebol.libertadores); the field description and the "Add More Leagues" hint now list eng.2 and friends.
  • manager.py — new _normalize_custom_leagues() strips the nulls (so every downstream .get(key, default) falls back to its default), splits team strings into lists, and trims/lowercases the league code. It runs from _load_custom_leagues(), i.e. on startup and on runtime config change. A null modes previously also crashed supports_dynamic_duration().
  • README.md — documents adding a league, that a row needs both a name and a code, and a table of common ESPN codes.

Test plan

  • Loaded the plugin in LEDMatrix on real hardware
  • Loaded the plugin in LEDMatrix emulator mode
    (EMULATOR=true python3 run.py)
  • Rendered the plugin in the dev preview server
    (scripts/dev_server.py)
  • Verified the web UI configuration form against the schema

Details:

  • New test_custom_league_config.py rebuilds the exact payload array-table.js submits for a new row (same String() / coerceValue() round-trip) and asserts it validates against the schema and normalizes correctly — 32 checks, all passing. It fails on the old schema with the four errors above.
  • Core safety harness on all sizes: all screens PASS. Re-ran with the raw web-UI payload (nulls included) as a --config override — soccer_eng.2_live/recent/upcoming register and render.
  • Existing plugin tests (test_live_mode_targeting, test_live_screens, test_goal_celebration, test_non_favorite_live_duration, test_timezone_resolution, test_world_cup_flags) still pass.
  • python scripts/check_module_collisions.py → OK across 42 plugins.

Required for plugin changes

  • Bumped version in plugins/<id>/manifest.json (2.4.0 → 2.4.1)
  • class_name in manifest.json matches the actual class in manager.py exactly
  • entry_point matches the real file (or is omitted to use the manager.py default)
  • Updated the plugin's README.md if config keys changed
  • config_schema.json is the source of truth for the web UI form — any new option is in the schema with a default, description, and constraints
  • Pre-commit hook ran successfully (auto-syncs plugins.json)

Checklist

  • My commits follow the message convention in CONTRIBUTING.md
  • I read CONTRIBUTING.md and CODE_OF_CONDUCT.md
  • I've not committed any secrets

Notes for reviewer

Fixed in the plugin rather than the core so existing installs get it through a plugin-store update. The same widget behaviour affects any plugin using x-widget: array-table with array/object properties outside x-columns; countdown and stock-news were checked and are not affected today.

The Championship stays a custom league rather than becoming a predefined one — that keeps the change to the bug, and the custom-league path is the designed mechanism. Happy to promote eng.2 into leagues if you'd rather it ship with its own settings block.


Generated by Claude Code

Adding any league under "Add More Leagues" failed to save with HTTP 400,
regardless of the league code entered — so the English Championship (eng.2)
could not be configured at all.

The web UI's array-table row editor stores every non-column property of a row
in a hidden text input: an empty input comes back as null, and a list property
comes back as the comma-separated string the user typed. custom_leagues
declared those properties as strict "array"/"object"/"number", so a brand-new
row submitted favorite_teams=null, exclude_teams=null,
dynamic_duration.max_duration_seconds=null and dynamic_duration.modes=null,
and the core's Draft-7 validation rejected the whole config.

- Schema: the properties the row editor can leave empty now accept null, and
  the team lists also accept the comma-separated text the editor produces.
- Manager: _normalize_custom_leagues() strips those nulls (so every downstream
  .get(key, default) falls back to its default), splits team strings back into
  lists, and trims/lowercases the league code. It runs from
  _load_custom_leagues(), i.e. on both startup and runtime config change.
  Previously a null "modes" also crashed supports_dynamic_duration().
- league_code pattern no longer rejects valid ESPN codes with underscores or
  prefixes longer than four letters (eng.league_cup, conmebol.libertadores).

test_custom_league_config.py rebuilds the exact payload array-table.js submits
for a new row and asserts it validates and normalizes correctly (32 checks).
Also verified end-to-end with the core safety harness: feeding the raw web-UI
payload as a config override registers and renders soccer_eng.2_live/recent/
upcoming.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FNF7jfQcu6dF63weiNZH93
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@ChuckBuilds, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 36 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: bdb7d26c-c7c8-424d-8ede-55dcbdc79203

📥 Commits

Reviewing files that changed from the base of the PR and between d23c03f and 564883b.

📒 Files selected for processing (6)
  • plugins.json
  • plugins/soccer-scoreboard/README.md
  • plugins/soccer-scoreboard/config_schema.json
  • plugins/soccer-scoreboard/manager.py
  • plugins/soccer-scoreboard/manifest.json
  • plugins/soccer-scoreboard/test_custom_league_config.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codacy-production

codacy-production Bot commented Jul 31, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 48 complexity

Metric Results
Complexity 48

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

Codacy flagged one new critical ErrorProne issue: pylint reads
`SoccerScoreboardPlugin._adapt_config_for_custom_league.__get__(plugin)(...)`
as an unbound call missing two arguments (E1120). The manual descriptor
binding was pointless — the test's plugin object is a real instance, so it can
call the method directly.

Also swaps the unused `import jsonschema` availability probe for
importlib.util.find_spec and renames two locals that shadowed module-level
names. No behavior change; still 32/32 checks passing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FNF7jfQcu6dF63weiNZH93
@ChuckBuilds
ChuckBuilds merged commit 43c1ed3 into main Jul 31, 2026
4 checks passed
@ChuckBuilds
ChuckBuilds deleted the claude/championship-football-config-2klztd branch July 31, 2026 17:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants