Основные изменения: - swiss.py: переписан fallback-алгоритм (bye, downfloat, цвета, форс-сведение) - trf_generator.py: генератор TRF-16 для bbpPairings - bbp_wrapper.py: обёртка subprocess для bbpPairings.exe - parser.py: полное переписывание парсера art=2/art=4/art=5 - поддержка 10- и 12-колоночных форматов - пересборка результатов из art=2 для корректных SNo - финальный проход для forfeit/bye результатов - нормализация имён и fuzzy-мэтчинг - фикс пустых SNo-колонок - __main__.py: починен JSON-вывод, поддержка bye - display.py: отображение bye и источника расчёта Ключевой фикс точности: убран XXC rank из TRF — bbpPairings теперь использует порядок из турнирной таблицы вместо поля Rank. Проверено на 5 турнирах (4 из 5 — 100%, 1 — 85% из-за ручных bye).
103 lines
4 KiB
Python
103 lines
4 KiB
Python
#!/usr/bin/env python3
|
||
"""End-to-end test: parse rounds 1-4, calculate round 5, compare with site."""
|
||
|
||
import sys
|
||
sys.path.insert(0, '.')
|
||
|
||
from swiss_calc.parser import fetch_url, parse_round_pairings, fetch_tournament
|
||
from swiss_calc.swiss import calculate_next_round
|
||
|
||
BASE = 'https://s1.chess-results.com/tnr1393124.aspx?lan=11'
|
||
|
||
print("=" * 60)
|
||
print("ТЕСТ: парсинг туров 1-4 → расчёт тура 5 vs chess-results.com")
|
||
print("=" * 60)
|
||
|
||
# 1. Parse tournament (rounds 1-4 from standings)
|
||
print("\n📥 Парсим турнир...")
|
||
data = fetch_tournament(BASE)
|
||
print(f" Турнир: {data['name'][:60]}")
|
||
print(f" Сыграно туров: {data['current_round']}")
|
||
print(f" Участников: {len(data['standings'])}")
|
||
|
||
# Show first few players' data
|
||
for s in data['standings'][:3]:
|
||
results_str = ' '.join(f"{r['opponent']}{r['color']}{r['score']}" for r in s['results'])
|
||
print(f" #{s['rank']} {s['name'][:30]}: {s['points']}pts, results=[{results_str}]")
|
||
|
||
# 2. Calculate round 5
|
||
print(f"\n🧮 Считаем тур {data['current_round'] + 1}...")
|
||
result = calculate_next_round(data)
|
||
print(f" Источник: {result['source']}")
|
||
calc_pairs = result['pairings']
|
||
print(f" Рассчитано пар: {len(calc_pairs)}")
|
||
|
||
# 3. Fetch actual round 5 from chess-results
|
||
print(f"\n📥 Парсим реальные пары тура 5 с сайта...")
|
||
html5 = fetch_url(f'{BASE}&art=2&rd=5&turdet=YES')
|
||
real_games = parse_round_pairings(html5, 5)
|
||
print(f" Всего игр на сайте: {len(real_games)}")
|
||
|
||
# Build sets for comparison
|
||
calc_set = set()
|
||
for wp, bp, color in calc_pairs:
|
||
sno_w = getattr(wp, 'sno', wp.get('starting_sno', 0)) if not hasattr(wp, 'sno') else wp.sno
|
||
sno_b = getattr(bp, 'sno', bp.get('starting_sno', 0)) if not hasattr(bp, 'sno') else bp.sno
|
||
calc_set.add((min(sno_w, sno_b), max(sno_w, sno_b)))
|
||
|
||
real_set = set()
|
||
for g in real_games:
|
||
w, b = g['white_sno'], g['black_sno']
|
||
real_set.add((min(w, b), max(w, b)))
|
||
|
||
# 4. Compare
|
||
matched = calc_set & real_set
|
||
extra_calc = calc_set - real_set
|
||
extra_real = real_set - calc_set
|
||
|
||
print(f"\n📊 СРАВНЕНИЕ:")
|
||
print(f" Совпало пар: {len(matched)}/{len(real_games)} ({100*len(matched)/max(len(real_games),1):.0f}%)")
|
||
print(f" Лишних в расчёте: {len(extra_calc)}")
|
||
print(f" Лишних на сайте: {len(extra_real)}")
|
||
|
||
if extra_calc:
|
||
print(f"\n Пары только в расчёте (первые 5):")
|
||
for w, b in list(extra_calc)[:5]:
|
||
print(f" #{w} vs #{b}")
|
||
|
||
if extra_real:
|
||
print(f"\n Пары только на сайте (первые 5):")
|
||
for w, b in list(extra_real)[:5]:
|
||
print(f" #{w} vs #{b}")
|
||
|
||
# 5. Show top boards side by side
|
||
print(f"\n📋 ДОСКИ 1-5: расчёт vs сайт")
|
||
calc_by_board = {}
|
||
for i, (wp, bp, color) in enumerate(calc_pairs):
|
||
sno_w = wp.sno
|
||
sno_b = bp.sno
|
||
calc_by_board[f"#{sno_w} #{wp.name[:20]}"] = f"#{sno_b} {bp.name[:20]}"
|
||
|
||
for g in real_games[:5]:
|
||
b = g['board']
|
||
w_name = g['white_name'][:25]
|
||
b_name = g['black_name'][:25]
|
||
ws, bs = g['white_sno'], g['black_sno']
|
||
# Find matching calc pair
|
||
pair_key = (min(ws, bs), max(ws, bs))
|
||
status = "✅" if pair_key in matched else "❌"
|
||
print(f" Доска {b}: #{ws} {w_name} vs #{bs} {b_name} {status}")
|
||
|
||
# 6. Check Vrubel specifically
|
||
print(f"\n🎯 Врубель Константин:")
|
||
for wp, bp, color in calc_pairs:
|
||
if 'Врубель' in wp.name:
|
||
print(f" Расчёт: #{wp.sno} {wp.name} (белые) vs #{bp.sno} {bp.name}")
|
||
break
|
||
for g in real_games:
|
||
if 'Врубель' in g.get('white_name', '') or 'Врубель' in g.get('black_name', ''):
|
||
if 'Врубель' in g['white_name']:
|
||
print(f" Сайт: #{g['white_sno']} {g['white_name']} (белые) vs #{g['black_sno']} {g['black_name']}")
|
||
else:
|
||
print(f" Сайт: #{g['black_sno']} {g['black_name']} (чёрные) vs #{g['white_sno']} {g['white_name']}")
|
||
break
|