diff --git a/core/sheets.py b/core/sheets.py index d490895..8eba97c 100644 --- a/core/sheets.py +++ b/core/sheets.py @@ -362,7 +362,11 @@ def parse_team_sections(rows: list[list]) -> list[TeamSection]: lineup_col = _find_col(rows[i], lambda c: c.strip().lower().startswith(_LINEUP_HEADER_PREFIX)) if name_col is None or lineup_col is None: if (text := _merged_title(rows[i])) is not None: - title = text + # Accumulate every single-cell row above the header, not just the + # last one: sheets stack the "TipiLAN 2026 CS2" title above notice + # rows ("If a team withdraws..."), and the later notices must not + # clobber the title whose keywords resolve_divider needs. + title = f"{title} {text}".strip() if title else text i += 1 continue rosters: dict[str, list[str]] = {} @@ -444,7 +448,10 @@ def _refresh_teams_sync() -> dict[str, list[str]]: dividers: dict[str, int] = {} for ws in spreadsheet.worksheets(): for section in parse_team_sections(ws.get_all_values()): - divider = resolve_divider(section.title) + # Match on the tab name too ("CS2"/"LoL"): it names the game reliably + # even when the game/year title row is shadowed by notice rows, while + # the section title still supplies the year for year-scoped dividers. + divider = resolve_divider(f"{ws.title} {section.title}") for team, players in section.rosters.items(): rosters.setdefault(team, []).extend(players) if divider: diff --git a/tests/test_team_sync.py b/tests/test_team_sync.py index 675a7f9..9dc740a 100644 --- a/tests/test_team_sync.py +++ b/tests/test_team_sync.py @@ -142,6 +142,22 @@ def test_resolve_divider_no_config_or_no_title_is_none(): assert sheets.resolve_divider("", DIVIDERS) is None +def test_title_survives_notice_rows_below_it(): + # Real sheets stack the game/year title above single-cell notice rows; the + # title's keywords must survive so resolve_divider still matches (regression: + # notice rows used to overwrite the title, yielding a None divider). + rows = [ + ["TipiLAN 2026 CS2 Registration Log"] + [""] * 4, + ["This log is updated automatically."] + [""] * 4, + ["If a team from the Top 32 withdraws, ..."] + [""] * 4, + ["No", "Team Name", "Lineup (nickname, citizenship)", "", ""], + ["1", "GENESIS", "kapa (EST)", "", ""], + ] + section = sheets.parse_team_sections(rows)[0] + assert "CS2" in section.title and "2026" in section.title + assert sheets.resolve_divider(section.title, DIVIDERS) == 1498736834656604251 + + # --- plan_team_positions (pure role-ordering maths) ------------------------ def test_plan_moves_teams_directly_under_their_divider():