#!/usr/bin/env python3 """Test round-by-round: parse once, truncate results, calculate next round.""" import sys sys.path.insert(0, '.') from swiss_calc.parser import fetch_tournament, fetch_url, parse_round_pairings from swiss_calc.trf_generator import generate_trf from swiss_calc.bbp_wrapper import call_bbp import re def norm(n): return re.sub(r'\s+', ' ', n.replace(',', '')).strip() BASE = 'https://s1.chess-results.com/tnr1289471.aspx?lan=11' print("=" * 60) print("ТЕСТ: пошаговый расчёт всех туров") print("=" * 60) # Parse once — get full data with correct SNo, byes, forfeits data = fetch_tournament(BASE) total_rounds = data['current_round'] standings = data['standings'] print(f"Турнир: {data['name'][:60]}") print(f"Сыграно туров: {total_rounds}") print(f"Участников: {len(standings)}") # Collect actual pairs for comparison name_to_sno = {norm(s['name']): s['starting_sno'] for s in standings} actual_pairs = {} for rd in range(1, total_rounds + 1): html = fetch_url(f'{BASE}&art=2&rd={rd}&turdet=YES') games = parse_round_pairings(html, rd) for g in games: if g['white_sno'] == 0: g['white_sno'] = name_to_sno.get(norm(g['white_name']), 0) if g['black_sno'] == 0: g['black_sno'] = name_to_sno.get(norm(g['black_name']), 0) actual_pairs[rd] = games print(f" Тур {rd}: {len(games)} пар") print(f"\n{'='*60}") print(f"ПОШАГОВЫЙ РАСЧЁТ") print(f"{'='*60}") overall_matched = 0 overall_total = 0 for next_rd in range(2, total_rounds + 1): completed = next_rd - 1 # Truncate results to only first `completed` rounds truncated_standings = [] for s in standings: full_results = s.get('results', []) full_orig = s.get('_orig_results', []) # Take results only up to round `completed` trunc_results = [r for r in full_results if r.get('round', 99) <= completed] trunc_orig = [r for r in full_orig if isinstance(r, dict) and r.get('round', 99) <= completed] points = sum(r['score'] for r in trunc_results) truncated_standings.append({ 'rank': s['rank'], 'name': s['name'], 'fed': s['fed'], 'points': points, 'starting_sno': s['starting_sno'], 'rating': s.get('rating', 0), 'results': trunc_results, '_orig_results': trunc_orig, 'tb': s.get('tb', []), }) truncated = { 'name': data['name'], 'num_rounds': total_rounds, 'current_round': completed, 'players': data['players'], 'standings': truncated_standings, } trf = generate_trf(truncated, next_rd, name=data.get('name', 'Test'), use_rank=True, initial_color_white=True) try: bbp_pairs, _ = call_bbp(trf) except Exception as e: print(f" Тур {next_rd}: bbpPairings ОШИБКА — {e}") continue # Compare with actual calc_set = set() for w_sno, b_sno in bbp_pairs: if b_sno == 0 or w_sno == 0: continue calc_set.add((min(w_sno, b_sno), max(w_sno, b_sno))) real_set = set() for g in actual_pairs.get(next_rd, []): w, b = g['white_sno'], g['black_sno'] if w == 0 or b == 0: continue # Only include completed games (not future pairings) if g.get('white_score') is not None: real_set.add((min(w, b), max(w, b))) matched = len(calc_set & real_set) total = len(real_set) if total == 0: print(f" Тур {next_rd}: нет сыгранных пар для сравнения") continue pct = 100 * matched / total overall_matched += matched overall_total += total bar = '█' * int(pct / 5) + '░' * (20 - int(pct / 5)) print(f" Тур {next_rd}: {matched}/{total} совпадений [{bar}] {pct:.0f}%") print(f"\n{'='*60}") if overall_total > 0: print(f"ИТОГО: {overall_matched}/{overall_total} ({100*overall_matched/overall_total:.0f}%)") else: print(f"ИТОГО: 0 пар") print(f"{'='*60}")