diff --git a/app.py b/app.py index 6155c0c..f1315c8 100644 --- a/app.py +++ b/app.py @@ -29,7 +29,7 @@ from ui_builder import install_ui_builder from khl_site.khl_data_center import APP as khl_site_app BASE_DIR = Path(__file__).resolve().parent -BUILD_VERSION = "2026.08.20.12" +BUILD_VERSION = "2026.08.20.13" # compatibility: BUILD_VERSION = "2026.08.19.28" # compatibility: BUILD_VERSION = "2026.08.19.24" # compatibility: BUILD_VERSION = "2026.08.19.22" diff --git a/hockey_data/service.py b/hockey_data/service.py index dd634a4..8037542 100644 --- a/hockey_data/service.py +++ b/hockey_data/service.py @@ -5537,20 +5537,25 @@ class HockeyDataService: elif state_key not in {"5x5", ""}: state_label = state_key.replace("x", " on " if language_key == "en" else " на ") - # BUILD91: Mapping captions describe both sides independently. The - # numerical-advantage side keeps the configured PP/state caption, while - # a penalized side that is not the advantage side gets a plain Penalty - # caption. This does not control whether the television penalty plate is - # on air; that remains the single-plate logic in the runtime. - penalty_caption = "Penalty" if language_key == "en" else "Удаление" + # BUILD92: Mapping captions describe the current numerical condition. + # The advantage side keeps its configured state caption (PP, "Power play", + # etc.), while a penalized non-advantage side receives the numerical + # condition itself (5 на 4 / 5 on 4, 5 на 3 / 5 on 3, ...). For an + # equal coincidental state both penalized sides receive the same numerical + # condition. This is Mapping-only data; television output still follows + # the single-penalty-plate runtime logic. + numerical_state_label = ( + state_key.replace("x", " on " if language_key == "en" else " на ") + if state_key else "" + ) home_label = ( state_label if advantage_side == "home" - else penalty_caption if home_penalties > 0 + else numerical_state_label if home_penalties > 0 else "" ) away_label = ( state_label if advantage_side == "away" - else penalty_caption if away_penalties > 0 + else numerical_state_label if away_penalties > 0 else "" ) phase_labels = { diff --git a/tests/test_build81_penalty_mapping_sort_and_mutual_labels.py b/tests/test_build81_penalty_mapping_sort_and_mutual_labels.py index 2121ee7..4141e71 100644 --- a/tests/test_build81_penalty_mapping_sort_and_mutual_labels.py +++ b/tests/test_build81_penalty_mapping_sort_and_mutual_labels.py @@ -62,5 +62,5 @@ def test_coincidental_penalties_do_not_fill_side_powerplay_labels(): value = HockeyDataService._strength_payload(settings, stage="regular", current_period="1", timer_state=timer_state, language="ru") assert value["advantage_side"] == "" assert value["state_key"] == "4x4" - assert value["home_label"] == "Удаление" - assert value["away_label"] == "Удаление" + assert value["home_label"] == "4 на 4" + assert value["away_label"] == "4 на 4" diff --git a/tests/test_build88_complex_penalty_display.py b/tests/test_build88_complex_penalty_display.py index 20af892..7b2b863 100644 --- a/tests/test_build88_complex_penalty_display.py +++ b/tests/test_build88_complex_penalty_display.py @@ -32,7 +32,7 @@ def test_complex_two_vs_one_keeps_real_strength_and_outputs_caption_on_advantage ) assert value["strength_label"] == "3×4" assert value["advantage_side"] == "away" - assert value["home_label"] == "Удаление" + assert value["home_label"] == "4 на 3" assert value["away_label"] == "Играют в большинстве" @@ -50,7 +50,7 @@ def test_single_remaining_penalty_outputs_pp_caption_on_opposite_team(): language="ru", ) assert value["advantage_side"] == "away" - assert value["home_label"] == "Удаление" + assert value["home_label"] == "5 на 4" assert value["away_label"] == "Играют в большинстве" diff --git a/tests/test_build89_single_penalty_plate_transition.py b/tests/test_build89_single_penalty_plate_transition.py index f823c4c..f81aa6d 100644 --- a/tests/test_build89_single_penalty_plate_transition.py +++ b/tests/test_build89_single_penalty_plate_transition.py @@ -32,8 +32,8 @@ def test_equal_coincidental_penalties_have_no_side_caption(): ) assert payload["strength_label"] == "4×4" assert payload["advantage_side"] == "" - assert payload["home_label"] == "Удаление" - assert payload["away_label"] == "Удаление" + assert payload["home_label"] == "4 на 4" + assert payload["away_label"] == "4 на 4" def test_two_vs_one_penalties_caption_only_advantage_team(): @@ -55,7 +55,7 @@ def test_two_vs_one_penalties_caption_only_advantage_team(): ) assert payload["strength_label"] == "3×4" assert payload["advantage_side"] == "away" - assert payload["home_label"] == "Удаление" + assert payload["home_label"] == "4 на 3" assert payload["away_label"] == "Играют в большинстве" diff --git a/tests/test_build91_runtime_version_pbp_penalty_labels.py b/tests/test_build91_runtime_version_pbp_penalty_labels.py index b86842d..3360f6e 100644 --- a/tests/test_build91_runtime_version_pbp_penalty_labels.py +++ b/tests/test_build91_runtime_version_pbp_penalty_labels.py @@ -54,7 +54,7 @@ def test_runtime_badge_is_server_build_version_placeholder(): assert "BUILD __UI_BUILDER_BUILD_VERSION__" in INDEX assert '.replace("__UI_BUILDER_BUILD_VERSION__", str(build_version or asset_version))' in INTEGRATION assert "build_version=BUILD_VERSION" in APP - assert 'BUILD_VERSION = "2026.08.20.12"' in APP + assert 'BUILD_VERSION = "2026.08.20.13"' in APP def test_play_by_play_defaults_collapsed_until_operator_opens_it(): @@ -62,20 +62,20 @@ def test_play_by_play_defaults_collapsed_until_operator_opens_it(): assert ": true;" in APP_JS -def test_penalized_side_gets_penalty_caption_and_advantage_side_keeps_pp_caption(): +def test_penalized_side_gets_numeric_condition_and_advantage_side_keeps_pp_caption(): payload = _strength(home=1, away=0) - assert payload["home_label"] == "Удаление" + assert payload["home_label"] == "5 на 4" assert payload["away_label"] == "Играют в большинстве" -def test_equal_coincidental_penalties_expose_penalty_caption_on_both_mapping_sides(): +def test_equal_coincidental_penalties_expose_numeric_condition_on_both_mapping_sides(): payload = _strength(home=1, away=1) assert payload["advantage_side"] == "" - assert payload["home_label"] == "Удаление" - assert payload["away_label"] == "Удаление" + assert payload["home_label"] == "4 на 4" + assert payload["away_label"] == "4 на 4" -def test_english_penalty_caption(): +def test_english_penalty_side_uses_numeric_condition(): payload = _strength(home=1, away=0, language="en") - assert payload["home_label"] == "Penalty" + assert payload["home_label"] == "5 on 4" assert payload["away_label"] == "Power play" diff --git a/tests/test_build92_numerical_penalty_labels.py b/tests/test_build92_numerical_penalty_labels.py new file mode 100644 index 0000000..0ca66ce --- /dev/null +++ b/tests/test_build92_numerical_penalty_labels.py @@ -0,0 +1,57 @@ +from hockey_data.service import HockeyDataService + + +def penalty(side): + return { + "side": side, + "infraction": {"id": "minor"}, + "preset": "2m", + "durationMs": 120000, + "remainingMs": 120000, + "finished": False, + } + + +def payload(home, away, labels=None): + settings = { + "strength_regulation_skaters": 5, + "strength_regular_overtime_skaters": 3, + "strength_playoff_overtime_skaters": 5, + "strength_min_skaters": 3, + "strength_state_labels": labels or { + "regulation": { + "5x4": {"ru": "Играют в большинстве"}, + "5x3": {"ru": "5 на 3"}, + "4x4": {"ru": "4 на 4"}, + "4x3": {"ru": "4 на 3"}, + } + }, + } + timer_state = {"penalty_board": {"penalties": [penalty("home") for _ in range(home)] + [penalty("away") for _ in range(away)]}} + return HockeyDataService._strength_payload(settings, stage="regular", current_period="1", timer_state=timer_state, language="ru") + + +def test_one_home_penalty_uses_5_on_4_on_penalized_side(): + result = payload(1, 0) + assert result["home_label"] == "5 на 4" + assert result["away_label"] == "Играют в большинстве" + + +def test_two_home_penalties_use_5_on_3_condition(): + result = payload(2, 0) + assert result["home_label"] == "5 на 3" + assert result["away_label"] == "5 на 3" + + +def test_equal_mutual_penalties_use_equal_numeric_condition_on_both_sides(): + result = payload(1, 1) + assert result["home_label"] == "4 на 4" + assert result["away_label"] == "4 на 4" + + +def test_complex_two_vs_one_keeps_majority_caption_and_numeric_penalty_side(): + labels = {"regulation": {"4x3": {"ru": "Большинство 4 на 3"}}} + result = payload(2, 1, labels) + assert result["advantage_side"] == "away" + assert result["home_label"] == "4 на 3" + assert result["away_label"] == "Большинство 4 на 3"