feat: pairings output - show ±5 around subscribed player
- _build_table_rows: filter to ±5 boards around player
- Normalize names (strip commas) for matching FIDE vs pairings format
- Highlight subscribed player with ✅ emoji
- Reduce name column width to compensate for emoji
- Add tournament name to pairing header
- Manual URL flow: look up subscription to highlight player
- Fix MarkdownV2 escaping for em-dash
This commit is contained in:
parent
89f5861ee3
commit
ba293dcb70
2 changed files with 73 additions and 14 deletions
|
|
@ -104,12 +104,15 @@ def _pad_col(s: str, width: int) -> str:
|
|||
return s + ' ' * (width - cur)
|
||||
|
||||
|
||||
def format_pairings(pairings_data: dict, tournament_name: str, lang: str = '') -> str:
|
||||
def format_pairings(pairings_data: dict, tournament_name: str, lang: str = '',
|
||||
player_name: str = '') -> str:
|
||||
locale = _get_locale(lang)
|
||||
pairings = pairings_data['pairings']
|
||||
rnd = pairings_data['round']
|
||||
|
||||
w_board, w_name, w_rating, w_pts, w_tb1 = 2, 17, 4, 4, 5
|
||||
# If highlighting a player, reduce name width to compensate for ✅ emoji (visually 2 chars wide)
|
||||
w_name = 15 if player_name else 17
|
||||
w_board, w_rating, w_pts, w_tb1 = 2, 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(locale["player"], w_name)}|'
|
||||
|
|
@ -121,23 +124,60 @@ def format_pairings(pairings_data: dict, tournament_name: str, lang: str = '') -
|
|||
footer = locale['footer_bbp'] if source == 'bbp_pairings_fide_2025' else \
|
||||
locale['footer_fallback'] if source == 'swiss_algorithm_simplified' else ''
|
||||
|
||||
safe_tour = _md_escape(tournament_name)
|
||||
title = f'📋 *{safe_tour}*\n*{locale["title"].format(rnd=rnd)}*'
|
||||
|
||||
return {
|
||||
'title': locale['title'].format(rnd=rnd),
|
||||
'title': title,
|
||||
'rows': _build_table_rows(pairings, w_board, w_name, w_rating, w_pts, w_tb1, border,
|
||||
locale['bye']),
|
||||
locale['bye'], player_name),
|
||||
'table_header': table_header,
|
||||
'footer': footer,
|
||||
}
|
||||
|
||||
|
||||
def _build_table_rows(pairings: list, w_board: int, w_name: int, w_rating: int,
|
||||
w_pts: int, w_tb1: int, border: str, bye_label: str = '— BYE —') -> list:
|
||||
w_pts: int, w_tb1: int, border: str, bye_label: str = '— BYE —',
|
||||
player_name: 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 ''
|
||||
|
||||
# Find the board where the player plays (normalize names: strip commas, collapse spaces)
|
||||
def _n(s: str) -> str:
|
||||
return s.replace(',', '').replace(' ', ' ').lower()
|
||||
|
||||
player_board = 0
|
||||
board_idx = 1
|
||||
player_norm = _n(player_name) if player_name else ''
|
||||
for pairing in pairings:
|
||||
if len(pairing) != 3:
|
||||
continue
|
||||
p1, p2, _color = pairing
|
||||
if player_norm and (player_norm == _n(p1.name) or player_norm == _n(p2.name)):
|
||||
player_board = board_idx
|
||||
# Store the actual name from pairings for the ✅ marker
|
||||
if player_norm == _n(p1.name):
|
||||
player_name = p1.name
|
||||
else:
|
||||
player_name = p2.name
|
||||
break
|
||||
board_idx += 1
|
||||
|
||||
# If we have a player, show ±5 boards around them
|
||||
if player_board > 0:
|
||||
lo = max(1, player_board - 5)
|
||||
hi = player_board + 5
|
||||
else:
|
||||
lo, hi = 1, 999999
|
||||
|
||||
def _name_cell(name: str) -> str:
|
||||
if player_name and name == player_name:
|
||||
return f'✅ {name}'
|
||||
return name
|
||||
|
||||
rows = []
|
||||
board = 1
|
||||
for pi, pairing in enumerate(pairings):
|
||||
|
|
@ -145,10 +185,16 @@ def _build_table_rows(pairings: list, w_board: int, w_name: int, w_rating: int,
|
|||
continue
|
||||
p1, p2, color = pairing
|
||||
|
||||
if board < lo:
|
||||
board += 1
|
||||
continue
|
||||
if board > hi:
|
||||
break
|
||||
|
||||
if color == 'bye':
|
||||
rows.append(
|
||||
f'{_pad_col(str(board), w_board)}|'
|
||||
f'{_pad_col(p1.name, w_name)}|'
|
||||
f'{_pad_col(_name_cell(p1.name), w_name)}|'
|
||||
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)}'
|
||||
|
|
@ -161,7 +207,7 @@ def _build_table_rows(pairings: list, w_board: int, w_name: int, w_rating: int,
|
|||
f'{_pad_col("", w_tb1)}'
|
||||
)
|
||||
board += 1
|
||||
if pi + 1 < len(pairings):
|
||||
if pi + 1 < len(pairings) and board <= hi:
|
||||
rows.append(border)
|
||||
continue
|
||||
|
||||
|
|
@ -172,22 +218,21 @@ def _build_table_rows(pairings: list, w_board: int, w_name: int, w_rating: int,
|
|||
|
||||
rows.append(
|
||||
f'{_pad_col(str(board), w_board)}|'
|
||||
f'{_pad_col(white.name, w_name)}|'
|
||||
f'{_pad_col(_name_cell(white.name), w_name)}|'
|
||||
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(_name_cell(black.name), w_name)}|'
|
||||
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
|
||||
|
||||
# Separator between pairs
|
||||
if pi + 1 < len(pairings):
|
||||
if pi + 1 < len(pairings) and board <= hi:
|
||||
rows.append(border)
|
||||
|
||||
return rows
|
||||
|
|
@ -342,7 +387,7 @@ async def _lookup_and_confirm(update: Update, context, fide_id: int):
|
|||
safe_tour = _md_escape(t['name'])
|
||||
lines.append(f' • {safe_tour}{dates}')
|
||||
lines.append('\nЯ буду присылать результаты и жеребьёвку\.\n'
|
||||
'/myplayers — список подписок')
|
||||
'/myplayers \\- список подписок')
|
||||
await update.message.reply_text('\n'.join(lines),
|
||||
parse_mode=ParseMode.MARKDOWN_V2)
|
||||
else:
|
||||
|
|
@ -607,7 +652,20 @@ async def handle_url(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
|||
return
|
||||
|
||||
lang_code = user.language_code or ''
|
||||
fmt = format_pairings(result, tournament['name'], lang_code)
|
||||
|
||||
# If user has a subscription for this tournament, highlight their player
|
||||
player_name = ''
|
||||
try:
|
||||
subs = tracker.get_user_subs(user.id)
|
||||
for s in subs:
|
||||
sub_tour = s.get('tournament_name', '')
|
||||
if sub_tour and sub_tour == tournament.get('name', ''):
|
||||
player_name = s.get('player_name', '')
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
fmt = format_pairings(result, tournament['name'], lang_code, player_name)
|
||||
chunks = _render_chunks(fmt)
|
||||
|
||||
try:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue