Парсер: поддержка латиницы (международные турниры) + таблица: колонки Рейт и Доп1
All checks were successful
CI/CD Pipeline / build-and-deploy (push) Successful in 8s
All checks were successful
CI/CD Pipeline / build-and-deploy (push) Successful in 8s
This commit is contained in:
parent
7f627beeec
commit
22a0e27763
2 changed files with 43 additions and 14 deletions
|
|
@ -78,10 +78,12 @@ def format_pairings(pairings_data: dict, tournament_name: str) -> str:
|
||||||
pairings = pairings_data['pairings']
|
pairings = pairings_data['pairings']
|
||||||
rnd = pairings_data['round']
|
rnd = pairings_data['round']
|
||||||
|
|
||||||
w_board, w_name, w_pts = 2, 17, 4
|
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_pts])
|
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'
|
table_header = f'```\n{header}\n{border}\n'
|
||||||
|
|
||||||
source = pairings_data.get('source', 'algorithm')
|
source = pairings_data.get('source', 'algorithm')
|
||||||
|
|
@ -90,14 +92,20 @@ def format_pairings(pairings_data: dict, tournament_name: str) -> str:
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'title': f'📋 *Тур {rnd}* — пары',
|
'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,
|
'table_header': table_header,
|
||||||
'footer': footer,
|
'footer': footer,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def _build_table_rows(pairings: list, w_board: int, w_name: int, w_pts: int,
|
def _build_table_rows(pairings: list, w_board: int, w_name: int, w_rating: int,
|
||||||
border: str) -> list:
|
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 = []
|
rows = []
|
||||||
board = 1
|
board = 1
|
||||||
for pi, pairing in enumerate(pairings):
|
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(
|
rows.append(
|
||||||
f'{_pad_col(str(board), w_board)}|'
|
f'{_pad_col(str(board), w_board)}|'
|
||||||
f'{_pad_col(p1.name, w_name)}|'
|
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(
|
rows.append(
|
||||||
f'{_pad_col("", w_board)}|'
|
f'{_pad_col("", w_board)}|'
|
||||||
f'{_pad_col("— BYE —", w_name)}|'
|
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
|
board += 1
|
||||||
if pi + 1 < len(pairings):
|
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(
|
rows.append(
|
||||||
f'{_pad_col(str(board), w_board)}|'
|
f'{_pad_col(str(board), w_board)}|'
|
||||||
f'{_pad_col(white.name, w_name)}|'
|
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(
|
rows.append(
|
||||||
f'{_pad_col("", w_board)}|'
|
f'{_pad_col("", w_board)}|'
|
||||||
f'{_pad_col(black.name, w_name)}|'
|
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
|
board += 1
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,19 @@ def _has_cyrillic(s: str) -> bool:
|
||||||
return bool(re.search(r'[а-яА-ЯёЁ]', s))
|
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]:
|
def parse_standings(html: str) -> List[Dict]:
|
||||||
"""Parse standings from art=4 or art=5 page.
|
"""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:
|
if sno < 1 or sno > 300:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Find name: look for Cyrillic text
|
# Find name: look for player name (Latin or Cyrillic)
|
||||||
name = ''
|
name = ''
|
||||||
name_idx = -1
|
name_idx = -1
|
||||||
for ci, t in enumerate(texts):
|
for ci, t in enumerate(texts):
|
||||||
if _has_cyrillic(t) and len(t) > 5:
|
if _is_player_name(t):
|
||||||
name = t
|
name = t
|
||||||
name_idx = ci
|
name_idx = ci
|
||||||
break
|
break
|
||||||
|
|
@ -517,7 +530,7 @@ def parse_start_list(html: str) -> dict:
|
||||||
for row in rows:
|
for row in rows:
|
||||||
cells = row.find_all('td')
|
cells = row.find_all('td')
|
||||||
texts = [c.get_text(strip=True) for c in cells]
|
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
|
data_rows += 1
|
||||||
if data_rows < 5:
|
if data_rows < 5:
|
||||||
continue
|
continue
|
||||||
|
|
@ -531,7 +544,7 @@ def parse_start_list(html: str) -> dict:
|
||||||
fed = ''
|
fed = ''
|
||||||
rating = 0
|
rating = 0
|
||||||
for ci, t in enumerate(texts[1:], 1):
|
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
|
name = t
|
||||||
if ci + 1 < len(texts) and re.match(r'^[A-Z]{3}$', texts[ci + 1]):
|
if ci + 1 < len(texts) and re.match(r'^[A-Z]{3}$', texts[ci + 1]):
|
||||||
fed = texts[ci + 1]
|
fed = texts[ci + 1]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue