Repository navigation
ufc: adopt core 3.5.0's shared sports mixins; golden images first (sports consolidation stage 1b) - #564
Merged
Conversation
…e the mixin adoption ufc-scoreboard had no test/harness.json, so CI's safety harness rendered it from schema defaults against live ESPN, and no golden could catch a visual change. This adds a deterministic fixture and 16 goldens (8 sizes x ufc_recent / ufc_upcoming), generated from origin/main's plugin on core v3.5.0, as the before-image for adopting core's shared sports mixins. The fixture seeds the season cache key BaseUFCManager reads first (ufc_scoreboard_schedule_2026) with cards either side of a frozen 2026-03-10 18:00 UTC: two finals and a postponed bout for Recent (the postponed one must stay off it), two scheduled bouts for Upcoming, and one card outside each window. Every bout has a seeded odds entry, so show_odds stays on without an odds request and the upcoming card draws a line. - display.use_short_date_format is on: the long date goes through the harness display manager's format_date_with_ordinal, whose %-d raises on Windows, and every bout then fails to parse. - Fighters carry no ESPN id, so no headshot is fetched; cards render without headshots, as they do for a fighter ESPN has none for. - Live is not rendered. The live manager is built and updated whatever show_live says, and _fetch_todays_games() is a direct ESPN call; test_mode plus a huge live_update_interval stops that fetch. The one request left is the headshot prefetch for test_mode's built-in fight (ESPN answers 404), which nothing rendered depends on: a run with DNS blocked produced byte-identical PNGs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r table
Core v3.5.0 (5b30052b) is tagged. Every module the table held as None
("in no tagged release") ships in it -- verified against
`git show v3.5.0:src/...` -- so each entry now names 3.5.0. The tag test
also found three modules missing from the table, split out of the store
manager in core #659 (store_install, store_registry, store_update); they
first ship in v3.5.0 too.
The test tracks v3.5.0 as a tag. Its "an unreleased module is reported"
case used sports_helpers, which is released now; it plants a fake None
entry instead, and two new cases pin a 3.5.0 module against 3.4.0 and
3.5.0 floors.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 6c19db1)
versions[0].ledmatrix_min_version and compatible_versions rise from 3.4.0 to 3.5.0, the first core release that ships src.common.espn_dates and src.common.sports_helpers. The following commits delete the bundled ufc_espn_dates.py and adopt core's shared sports mixins; the versions[] notes and the CHANGELOG entry describe the whole series, including its one behaviour change (the #599 idle wake-up). A floor raise is a minor bump, as for the eight team scoreboards in #563. plugins.json regenerated with update_registry.py. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lainly ufc bundled a copy of core's src/common/espn_dates.py behind a guarded import, for cores that predate the module. It now floors on 3.5.0, which ships it, so the copy can never run: deleted (385 lines), and the guard in sports.py and data_sources.py collapses to a plain `from src.common.espn_dates import ESPN_MAX_LIMIT, fetch_espn_scoreboard`. No behaviour change. On a 3.5.0 core the guard already took the core branch, so the functions the plugin calls are the ones it called before. The deleted copy matches core v3.5.0 once type annotations, typing imports and cast() wrappers are set aside (core #661's typing-only edits), the comparison #563's test_espn_dates_copies.py makes. scripts/test_espn_dates_copies.py required all nine copies. It gains a SUNSET_PLUGINS set -- the name #563 uses, so the two merge as a union -- holding ufc-scoreboard: its copy must stay deleted and no module of it may import a bundled *_espn_dates; its fetches are still scanned for a direct `dates` request. Checked by planting a copy and a bare import (both fail). The test's remaining failure against core v3.5.0 (the eight remaining copies differ from core in typing) is on origin/main too; #563 fixes it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… copies ufc's sports.py carried private copies of the helpers core 3.5.0 ships in src.common.sports_helpers: - module level: _MIN_WINDOW_DAYS, _MAX_WINDOW_DAYS, _clamp_window, _clamp_seconds, _logo_needs_refresh; - on SportsCore: _mode_customization, _setting_int, _DWELL_REENTRY_GAP_SECONDS, _reset_dwell_on_reentry, _spread_weighted_order, _next_switch_index. All deleted (the same treatment #563 gives the eight team scoreboards). SportsCore now lists SportsHelpersMixin before ABC. The module-level names are imported under their old private spellings, so sports.py's callers and the tests that call sports._clamp_seconds / sports._clamp_window are unchanged. No behaviour change: - check_sports_helpers_parity.py against core v3.5.0 found all 11 of ufc's copies identical to core's (normalised AST) before the deletion; it now reports the 13 names absent and the core module imported; - no UFC subclass (MMA*, UFC*Manager) defines any of these names, so every lookup that reached SportsCore's copy now reaches the mixin's; - the mixin adds _favorite_key, _odds_color and _upcoming_date_and_time_text, which nothing in ufc calls, and a class default favorite_rotation_boost = 1, which SportsCore.__init__ always overrides per instance and the one reader reads with getattr(..., 1); - all 16 harness renders are byte-identical and the 23 plugin tests pass. test_logo_placeholder_refresh.py required a local _logo_needs_refresh in every logo loader. A loader may now import core's under the private name instead (ADOPTED_RE, the pattern #563 uses); the byte-identity check skips those and still pins the copies that remain. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e copies ufc was the one scoreboard whose sports.py did not use core's src.common.sports_shared (3.3.0). SportsCore, SportsLive and SportsRecent now inherit SportsCoreSharedMixin, SportsLiveSharedMixin and SportsRecentSharedMixin, in the order the eight team scoreboards use, and their private copies are deleted (-442 lines): - identical to core's as a docstring-stripped AST (15): SportsCore _should_log, _custom_scorebug_layout, cleanup, _game_divisions, _league_has_rankings, _normalise_divisions, _normalise_quality, _check_ranking_coverage, _favorites_first, _compose_selection; SportsLive _detect_stale_games, _note_live_fetch; SportsRecent __init__, _get_zero_clock_duration, _clear_zero_clock_tracking; - same behaviour, different text (2): _get_weeks_data differs only by a leading `date_str = ""`, unreachable for the reason #563's c8224ec gives (nothing before the mixin's own assignment can raise the RequestException its handler catches); _round_robin_favorites names its matcher `matcher` instead of `team_in` and tests `is None` instead of `callable()` on the same getattr -- the same two closures chosen by the same condition; - _idle_live_interval (1): the one behaviour change, below. Kept, because core's behave differently: SportsCore._draw_scorebug_layout, _get_layout_offset (core's routes through src.element_style) and _draw_text_with_outline (core's takes element= and resolves colour from it). They are defined on SportsCore itself, which precedes the mixin in every MRO. SportsLiveSharedMixin and SportsRecentSharedMixin sit before SportsCore in SportsLive / SportsRecent, and SportsCore defines none of their names except __init__, where the Recent mixin's __init__ is the deleted SportsRecent.__init__ verbatim and calls on to SportsCore's. Behaviour change (the only one): the #599 idle wake-up. Before, ufc's _idle_live_interval was the pre-#599 body, and update()'s getattr(self, "_note_scheduled_start_candidate", None) found nothing, so an idle league sat at live_idle_max_interval_seconds (900s default) and could sleep through the first bout of a card. Now each live fetch offers every non-live event's start_time_utc (MMA._extract_game_details sets it from the card's date) to _note_scheduled_start_candidate, which keeps the earliest future start as _next_scheduled_start_ts; _idle_live_interval computes the same back-off as before and then clamps it: before a known start, to max(update_interval, seconds until it); within 15 minutes after it, to update_interval (live_update_interval, 30s by default); otherwise unchanged. No request is added. Driven end to end through the real UFCLiveManager.update with a scheduled card two hours out and 40 empty looks: before 900/900/900/900s at T-2h/T-2m/T+5m/T+1h, after 900/120/30/900s. test_idle_league_backoff.py gains those cases; they fail on the previous commit. Checked otherwise unchanged: - every attribute of all 12 UFC classes (SportsCore .. UFC*Manager) before and after: none lost; the rest resolve to core only where the body is the same (or is one of the three cases above); the mixins add attributes nothing in ufc reads (grep of every attribute access and getattr/hasattr string), apart from _note_scheduled_start_candidate; - the three managers built through the plugin from the harness config hold the same instance attributes with the same values; - all 16 harness renders byte-identical; 23 plugin tests pass. check_sports_drift.py reports SportsCore._league_has_rankings back in sync (ufc's was a divergent copy); baseline regenerated, dropping that one entry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
08-shared-sports-code.md counted nine bundled <sport>_espn_dates.py copies and said every scoreboard imports src.common.sports_shared, which was not true of ufc until now. The table row drops to the eight team scoreboards and names ufc's sunset; the sports_helpers section records that ufc (1.16.0, floor 3.5.0) inherits core's four mixins, what its sports.py keeps, and that the adoption brought it the #599 kickoff clamp. "Floors at 3.4.0" becomes "3.4.0 or above", the wording #563 uses too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tage1b-ufc-mixins #563 landed while this branch was open. Its scripts/check_min_core_version.py change is the commit this branch cherry-picked (d2c274b), so it merged cleanly. Three files conflicted: - scripts/test_espn_dates_copies.py: #563's version, with ufc-scoreboard added to its SUNSET_PLUGINS. All nine scoreboards are now in the set, so no bundled copy remains to compare; the docstring says so instead of "ufc still bundles one". - scripts/test_logo_placeholder_refresh.py: #563's version. It already accepts a loader that imports core's helper under the private name (ADOPTED_RE), and requires every loader to either define it or import it: swapping ufc's import for a local stub fails two tests. With no plugin copy left, the behaviour tests run against core's function. Docstring no longer says ufc carries a copy. - docs/plugin-development/08-shared-sports-code.md: merged by hand. The espn_dates row reads 0 copies (all nine sunset, 1a and 1b); "Converged at 3.5.0" covers all nine and says what ufc also adopted; the sports_helpers and sunset-history text name ufc as done instead of pending. Also fixed the remaining stale wording: CLAUDE.md non-negotiable #7 (all nine scoreboards import espn_dates and inherit the core mixins; no "ufc still bundles both"), check_sports_helpers_parity.py's docstring (no "ufc still carries its eleven"), and check_selection_settings.py's NOT_WIRED comment (ufc's selection code is now inherited from core's mixin). ufc's versions[0] is still 1.16.0 / floor 3.5.0 and update_registry.py --check passes. Against origin/main, both on core v3.5.0: the 16 ufc harness renders are byte-identical and match the goldens on both trees; run_plugin_tests.py for ufc plus the eight team scoreboards gives the same per-test results (322 passed; the same two football failures on both); all 52 scripts/test_*.py and check scripts pass on both trees. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: 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. Comment |
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 0 |
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stage 1 of the sports-code consolidation, part b. It follows #563 and moves UFC onto core v3.5.0's shared sports code.
Changes
ufc_espn_dates.pyremoved: the file (385 lines) is deleted and the imports are plain. It now matches core except for typing.ufc-scoreboardjoinsSUNSET_PLUGINS, which means none of the nine scoreboards bundles a copy any more.SportsHelpersMixin: inherited from core, and UFC's 11 identical copies are deleted.SportsCoreSharedMixin,SportsLiveSharedMixin,SportsRecentSharedMixin: adopted, and 18 methods are deleted._get_weeks_datadiffers only by an unreachabledate_str = ""._round_robin_favoriteshas one variable renamed._idle_live_intervalis the perf(football): keep the card renderer and card width between strip builds (3.18.3) #599 change, described below.SportsCore, which comes first in every class hierarchy:_draw_scorebug_layout,_get_layout_offsetand_draw_text_with_outline.versions[]entry, CHANGELOG,plugins.json).check_sports_helpers_parity.pyandcheck_selection_settings.py. The drift baseline drops one entry.Net: 32 files, +557 / −1,091 (
sports.pyis 3,947 → 3,297 lines).Behaviour change: the #599 idle wake-up
Until now, with nothing live, UFC's live check backed off to its 900 s maximum and could miss the first bout of a card. It now notes the earliest upcoming card start from the events it already downloads, and never waits past it. The wait never drops below the live interval (30 s by default). For 15 minutes after the start it polls at the live interval, and it makes no extra requests.
Driven through the real live manager with a card two hours away, the waits were:
New cases in
test_idle_league_backoff.pyfail on the old code.Verification
All runs used core v3.5.0, comparing origin/main against this branch.
run_plugin_tests.pyfor ufc plus the 8 team scoreboards gives 322 passed and 2 failed on both trees, with the same result per test. The 2 failures are pre-existing: football'stest_odds_placementandtest_football_plugin, which asks for input.scripts/test_*.pyand check scripts pass.check_version_bump --base origin/main ufc-scoreboardpasses, and so doesupdate_registry.py --check.🤖 Generated with Claude Code