Пробинг туров: если тур уже сыгран — продвигаемся дальше вместо остановки
All checks were successful
CI/CD Pipeline / build-and-deploy (push) Successful in 4s

This commit is contained in:
vrubel 2026-06-15 10:47:18 +00:00
parent 4b43432ecd
commit e0a897fd67

View file

@ -191,6 +191,76 @@ async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text(START_MSG, parse_mode=ParseMode.MARKDOWN_V2) await update.message.reply_text(START_MSG, parse_mode=ParseMode.MARKDOWN_V2)
def _apply_round_results(tournament, games, rd):
standings = tournament['standings']
sno_to_standing = {s['starting_sno']: s for s in standings if s.get('starting_sno')}
name_to_standing = {
re.sub(r'\s+', '', s['name'].lower()): s
for s in standings
}
name_parts_to_standing: dict[str, list] = {}
for s in standings:
parts = re.sub(r'\s+', '', s['name'].lower()).split()
for p in parts:
name_parts_to_standing.setdefault(p, []).append(s)
def _find_player(sno, name_str):
p = sno_to_standing.get(sno)
if p:
return p
if name_str:
norm = re.sub(r'\s+', '', name_str.lower())
p = name_to_standing.get(norm)
if p:
return p
parts = norm.split()
for part in parts:
candidates = name_parts_to_standing.get(part, [])
if len(candidates) == 1:
return candidates[0]
return None
for g in games:
if g is None:
continue
ws, bs = g['white_sno'], g['black_sno']
ws_result = g.get('white_score')
bs_result = g.get('black_score')
if ws_result is None:
continue
ws_val = float(ws_result)
bs_val = float(bs_result)
wp = _find_player(ws, g.get('white_name', ''))
bp = _find_player(bs, g.get('black_name', ''))
if wp:
opp_sno = bs if bs != 0 else (bp.get('starting_sno') if bp else 0)
existing = [r for r in wp.get('results', []) if r.get('round') != rd]
existing.append({'opponent': opp_sno, 'color': 'w', 'score': ws_val, 'round': rd})
wp['results'] = existing
if bp:
opp_sno = ws if ws != 0 else (wp.get('starting_sno') if wp else 0)
existing = [r for r in bp.get('results', []) if r.get('round') != rd]
existing.append({'opponent': opp_sno, 'color': 'b', 'score': bs_val, 'round': rd})
bp['results'] = existing
for s in standings:
sno = s.get('starting_sno')
if sno and len(s.get('results', [])) < rd:
orig_results = s.get('_orig_results', [])
if len(orig_results) >= rd:
res = orig_results[rd - 1]
res['round'] = rd
s['results'] = list(s.get('results', []))
s['results'].append(res)
tournament['current_round'] = rd
async def handle_url(update: Update, context: ContextTypes.DEFAULT_TYPE): async def handle_url(update: Update, context: ContextTypes.DEFAULT_TYPE):
user = update.effective_user user = update.effective_user
text = update.message.text.strip() text = update.message.text.strip()
@ -239,24 +309,37 @@ async def handle_url(update: Update, context: ContextTypes.DEFAULT_TYPE):
base_url = re.sub(r'[&?]turdet=\w+', '', base_url) base_url = re.sub(r'[&?]turdet=\w+', '', base_url)
base_url = re.sub(r'[&?]SNode=\w+', '', base_url) base_url = re.sub(r'[&?]SNode=\w+', '', base_url)
next_rd = tournament['current_round'] + 1 next_rd = tournament['current_round'] + 1
initial_rd = next_rd
from swiss_calc.parser import fetch_url as _fetch_url, parse_round_pairings from swiss_calc.parser import fetch_url as _fetch_url, parse_round_pairings
try: try:
while next_rd <= tournament['num_rounds']:
rd_html = _fetch_url(f'{base_url}&art=2&rd={next_rd}&turdet=YES') rd_html = _fetch_url(f'{base_url}&art=2&rd={next_rd}&turdet=YES')
rd_games = parse_round_pairings(rd_html, next_rd) rd_games = parse_round_pairings(rd_html, next_rd)
played = any(g.get('white_score') is not None for g in rd_games if g) played = any(g.get('white_score') is not None for g in rd_games if g)
if played: if played:
await update.message.reply_text( _apply_round_results(tournament, rd_games, next_rd)
f'⚠️ Тур {next_rd} уже сыгран — на сайте есть результаты\\.', next_rd += 1
parse_mode=ParseMode.MARKDOWN_V2) continue
record_request(user.id, url, tournament.get('name', ''), True)
return
if rd_games: if rd_games:
await update.message.reply_text( await update.message.reply_text(
f'⚠️ Жеребьёвка тура {next_rd} уже опубликована на chess\\-results\\.com\\.\n' f'⚠️ Жеребьёвка тура {next_rd} уже опубликована на chess\\-results\\.com\\.\n'
'Я всё равно посчитаю, но пары могут незначительно отличаться ' 'Я всё равно посчитаю, но пары могут незначительно отличаться '
'\\(разные редакции правил FIDE\\)\\.', '\\(разные редакции правил FIDE\\)\\.',
parse_mode=ParseMode.MARKDOWN_V2) parse_mode=ParseMode.MARKDOWN_V2)
break
else:
await msg.edit_text('🏁 Турнир уже завершён\\. Жеребьёвки не будет\\.',
parse_mode=ParseMode.MARKDOWN_V2)
record_request(user.id, url, tournament.get('name', ''), True)
return
if next_rd > initial_rd:
await msg.edit_text(
f'✅ *{_md_escape(tournament["name"])}*\n'
f'📅 Сыграно туров: {tournament["current_round"]} из {tournament["num_rounds"]}\n'
f'👥 Участников: {len(tournament["standings"])}',
parse_mode=ParseMode.MARKDOWN_V2)
except Exception: except Exception:
pass pass