From 22a0e27763e95c3d541e5ab8696990662e15eaa0 Mon Sep 17 00:00:00 2001 From: vrubel Date: Fri, 19 Jun 2026 14:51:15 +0000 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=B0=D1=80=D1=81=D0=B5=D1=80:=20=D0=BF?= =?UTF-8?q?=D0=BE=D0=B4=D0=B4=D0=B5=D1=80=D0=B6=D0=BA=D0=B0=20=D0=BB=D0=B0?= =?UTF-8?q?=D1=82=D0=B8=D0=BD=D0=B8=D1=86=D1=8B=20(=D0=BC=D0=B5=D0=B6?= =?UTF-8?q?=D0=B4=D1=83=D0=BD=D0=B0=D1=80=D0=BE=D0=B4=D0=BD=D1=8B=D0=B5=20?= =?UTF-8?q?=D1=82=D1=83=D1=80=D0=BD=D0=B8=D1=80=D1=8B)=20+=20=D1=82=D0=B0?= =?UTF-8?q?=D0=B1=D0=BB=D0=B8=D1=86=D0=B0:=20=D0=BA=D0=BE=D0=BB=D0=BE?= =?UTF-8?q?=D0=BD=D0=BA=D0=B8=20=D0=A0=D0=B5=D0=B9=D1=82=20=D0=B8=20=D0=94?= =?UTF-8?q?=D0=BE=D0=BF1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bots/client_bot.py | 36 ++++++++++++++++++++++++++---------- swiss_calc/parser.py | 21 +++++++++++++++++---- 2 files changed, 43 insertions(+), 14 deletions(-) diff --git a/bots/client_bot.py b/bots/client_bot.py index f9f1c31..916f5e0 100644 --- a/bots/client_bot.py +++ b/bots/client_bot.py @@ -78,10 +78,12 @@ def format_pairings(pairings_data: dict, tournament_name: str) -> str: pairings = pairings_data['pairings'] rnd = pairings_data['round'] - w_board, w_name, w_pts = 2, 17, 4 - border = '+'.join('-' * w for w in [w_board, w_name, w_pts]) + w_board, w_name, w_rating, w_pts, w_tb1 = 2, 17, 4, 4, 5 + border = '+'.join('-' * w for w in [w_board, w_name, w_rating, w_pts, w_tb1]) - header = f'{_pad_col("#", w_board)}|{_pad_col("Игрок", w_name)}|{_pad_col("О", w_pts)}' + header = (f'{_pad_col("#", w_board)}|{_pad_col("Игрок", w_name)}|' + f'{_pad_col("Рейт", w_rating)}|{_pad_col("О", w_pts)}|' + f'{_pad_col("Доп1", w_tb1)}') table_header = f'```\n{header}\n{border}\n' source = pairings_data.get('source', 'algorithm') @@ -90,14 +92,20 @@ def format_pairings(pairings_data: dict, tournament_name: str) -> str: return { 'title': f'📋 *Тур {rnd}* — пары', - 'rows': _build_table_rows(pairings, w_board, w_name, w_pts, border), + 'rows': _build_table_rows(pairings, w_board, w_name, w_rating, w_pts, w_tb1, border), 'table_header': table_header, 'footer': footer, } -def _build_table_rows(pairings: list, w_board: int, w_name: int, w_pts: int, - border: str) -> list: +def _build_table_rows(pairings: list, w_board: int, w_name: int, w_rating: int, + w_pts: int, w_tb1: int, border: str) -> list: + def _rating_str(p): + return str(p.rating) if p.rating else '' + + def _tb1_str(p): + return f"{p.tb[0]:.1f}" if p.tb else '' + rows = [] board = 1 for pi, pairing in enumerate(pairings): @@ -109,12 +117,16 @@ def _build_table_rows(pairings: list, w_board: int, w_name: int, w_pts: int, rows.append( f'{_pad_col(str(board), w_board)}|' f'{_pad_col(p1.name, w_name)}|' - f'{_pad_col(f"{p1.points:.1f}", w_pts)}' + f'{_pad_col(_rating_str(p1), w_rating)}|' + f'{_pad_col(f"{p1.points:.1f}", w_pts)}|' + f'{_pad_col(_tb1_str(p1), w_tb1)}' ) rows.append( f'{_pad_col("", w_board)}|' f'{_pad_col("— BYE —", w_name)}|' - f'{_pad_col("", w_pts)}' + f'{_pad_col("", w_rating)}|' + f'{_pad_col("", w_pts)}|' + f'{_pad_col("", w_tb1)}' ) board += 1 if pi + 1 < len(pairings): @@ -129,12 +141,16 @@ def _build_table_rows(pairings: list, w_board: int, w_name: int, w_pts: int, rows.append( f'{_pad_col(str(board), w_board)}|' f'{_pad_col(white.name, w_name)}|' - f'{_pad_col(f"{white.points:.1f}", w_pts)}' + f'{_pad_col(_rating_str(white), w_rating)}|' + f'{_pad_col(f"{white.points:.1f}", w_pts)}|' + f'{_pad_col(_tb1_str(white), w_tb1)}' ) rows.append( f'{_pad_col("", w_board)}|' f'{_pad_col(black.name, w_name)}|' - f'{_pad_col(f"{black.points:.1f}", w_pts)}' + f'{_pad_col(_rating_str(black), w_rating)}|' + f'{_pad_col(f"{black.points:.1f}", w_pts)}|' + f'{_pad_col(_tb1_str(black), w_tb1)}' ) board += 1 diff --git a/swiss_calc/parser.py b/swiss_calc/parser.py index a1da9b8..fb82ae7 100644 --- a/swiss_calc/parser.py +++ b/swiss_calc/parser.py @@ -82,6 +82,19 @@ def _has_cyrillic(s: str) -> bool: return bool(re.search(r'[а-яА-ЯёЁ]', s)) +def _is_player_name(t: str) -> bool: + """Check if text looks like a player name (Latin or Cyrillic).""" + if len(t) <= 5: + return False + if re.match(r'^\d+$', t): + return False + if re.match(r'^[A-Z]{3}$', t): + return False + if re.match(r'^\d+[bw][½\d]?$', t): + return False + return bool(re.search(r'[A-Za-zА-Яа-я]', t)) + + def parse_standings(html: str) -> List[Dict]: """Parse standings from art=4 or art=5 page. @@ -110,11 +123,11 @@ def parse_standings(html: str) -> List[Dict]: if sno < 1 or sno > 300: continue - # Find name: look for Cyrillic text + # Find name: look for player name (Latin or Cyrillic) name = '' name_idx = -1 for ci, t in enumerate(texts): - if _has_cyrillic(t) and len(t) > 5: + if _is_player_name(t): name = t name_idx = ci break @@ -517,7 +530,7 @@ def parse_start_list(html: str) -> dict: for row in rows: cells = row.find_all('td') texts = [c.get_text(strip=True) for c in cells] - if len(texts) >= 4 and texts[0].isdigit() and _has_cyrillic(' '.join(texts)): + if len(texts) >= 4 and texts[0].isdigit() and any(_is_player_name(t) for t in texts): data_rows += 1 if data_rows < 5: continue @@ -531,7 +544,7 @@ def parse_start_list(html: str) -> dict: fed = '' rating = 0 for ci, t in enumerate(texts[1:], 1): - if _has_cyrillic(t) and len(t) > 5 and not name: + if _is_player_name(t) and not name: name = t if ci + 1 < len(texts) and re.match(r'^[A-Z]{3}$', texts[ci + 1]): fed = texts[ci + 1]