Files
WFL/repositories/match_lineup_repository.py
2026-04-22 13:24:42 +03:00

461 lines
15 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from db import get_connection
from repositories.match_view_repository import get_match_lineups_grouped
from repositories.match_coach_repository import get_match_coaches_grouped
def replace_match_lineups(match_id: int, rows: list[dict]) -> None:
delete_query = "DELETE FROM match_lineups WHERE match_id = %s;"
insert_query = """
INSERT INTO match_lineups (
match_id,
team_id,
player_id,
player_name,
number,
position,
is_captain,
lineup_type,
source,
created_at,
updated_at
)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, NOW(), NOW());
"""
conn = get_connection()
try:
with conn.cursor() as cur:
cur.execute(delete_query, (match_id,))
for row in rows:
cur.execute(
insert_query,
(
row["match_id"],
row["team_id"],
row.get("player_id"),
row["player_name"],
row.get("number"),
row.get("position"),
row.get("is_captain"),
row["lineup_type"],
row.get("source", "parser"),
),
)
conn.commit()
except Exception:
conn.rollback()
raise
finally:
conn.close()
def _player_to_editor_dict(p: dict) -> dict:
return {
"player_id": p.get("player_id"),
"player_name": p.get("player_name")
or f"{p.get('last_name', '')} {p.get('first_name', '')}".strip(),
"last_name": p.get("last_name", "") or p.get("player_name", "") or "",
"first_name": p.get("first_name", "") or "",
"number": str(p.get("number", "") or ""),
"position": p.get("position", "") or "",
"is_captain": bool(p.get("is_captain")),
}
def _coach_to_editor_dict(c: dict) -> dict:
return {
"coach_id": c.get("coach_id"),
"coach_name": c.get("coach_name", "") or "",
"role": c.get("role", "") or "",
}
def get_match_lineup_for_editor(
match_id: int,
home_team_id: int,
away_team_id: int,
) -> dict:
conn = get_connection()
try:
with conn.cursor() as cur:
cur.execute(
"""
SELECT
mlp.side,
mlp.role,
mlp.sort_order,
p.id AS player_id,
COALESCE(p.full_name, TRIM(COALESCE(p.last_name, '') || ' ' || COALESCE(p.first_name, ''))) AS player_name,
COALESCE(p.last_name, '') AS last_name,
COALESCE(p.first_name, '') AS first_name,
COALESCE(mlp.number::text, '') AS number,
COALESCE(p.position, '') AS position,
COALESCE(mlp.is_captain, FALSE) AS is_captain
FROM match_lineup_players mlp
JOIN players p
ON p.id = mlp.player_id
WHERE mlp.match_id = %s
ORDER BY mlp.side, mlp.role, mlp.sort_order, mlp.id
""",
(match_id,),
)
player_rows = cur.fetchall()
cur.execute(
"""
SELECT
mc.side,
mc.sort_order,
c.id AS coach_id,
COALESCE(c.player, '') AS coach_name,
COALESCE(mc.role, c.amplua, '') AS role
FROM match_coaches mc
JOIN coaches c
ON c.id = mc.coach_id
WHERE mc.match_id = %s
ORDER BY mc.side, mc.sort_order, mc.id
""",
(match_id,),
)
coach_rows = cur.fetchall()
finally:
conn.close()
result = {
"home_starting": [],
"home_bench": [],
"away_starting": [],
"away_bench": [],
"home_coaches": [],
"away_coaches": [],
}
# Если редактор уже сохранял состав — берём игроков из новой таблицы
if player_rows:
for row in player_rows:
player = {
"player_id": row[3],
"player_name": row[4] or "",
"last_name": row[5] or "",
"first_name": row[6] or "",
"number": row[7] or "",
"position": row[8] or "",
"is_captain": bool(row[9]),
}
side = row[0]
role = row[1]
if side == "home" and role == "starting":
result["home_starting"].append(player)
elif side == "home" and role == "bench":
result["home_bench"].append(player)
elif side == "away" and role == "starting":
result["away_starting"].append(player)
elif side == "away" and role == "bench":
result["away_bench"].append(player)
else:
# Иначе берём загруженный с сайта состав из старой таблицы
lineups = get_match_lineups_grouped(
match_id=match_id,
home_team_id=home_team_id,
away_team_id=away_team_id,
)
result["home_starting"] = [
_player_to_editor_dict(p) for p in lineups.get("home_starting", [])
]
result["home_bench"] = [
_player_to_editor_dict(p) for p in lineups.get("home_bench", [])
]
result["away_starting"] = [
_player_to_editor_dict(p) for p in lineups.get("away_starting", [])
]
result["away_bench"] = [
_player_to_editor_dict(p) for p in lineups.get("away_bench", [])
]
# Тренеров берём из match_coaches, если они уже есть
if coach_rows:
for row in coach_rows:
coach = {
"coach_id": row[2],
"coach_name": row[3] or "",
"role": row[4] or "",
}
side = row[0]
if side == "home":
result["home_coaches"].append(coach)
elif side == "away":
result["away_coaches"].append(coach)
else:
coaches = get_match_coaches_grouped(
match_id=match_id,
home_team_id=home_team_id,
away_team_id=away_team_id,
)
result["home_coaches"] = [
_coach_to_editor_dict(c) for c in coaches.get("home", [])
]
result["away_coaches"] = [
_coach_to_editor_dict(c) for c in coaches.get("away", [])
]
return result
def get_match_lineup_for_vmix(
match_id: int,
home_team_id: int,
away_team_id: int,
) -> dict:
conn = get_connection()
try:
with conn.cursor() as cur:
# Сначала пробуем взять ручную версию из редактора
cur.execute(
"""
SELECT
mlp.side,
mlp.role,
mlp.sort_order,
p.id AS player_id,
COALESCE(
p.full_name,
TRIM(COALESCE(p.last_name, '') || ' ' || COALESCE(p.first_name, ''))
) AS player_name,
COALESCE(p.last_name, '') AS last_name,
COALESCE(p.first_name, '') AS first_name,
COALESCE(mlp.number::text, '') AS number,
COALESCE(mlp.position, '') AS position,
COALESCE(mlp.is_captain, FALSE) AS is_captain,
COALESCE(p.position, '') AS pos
FROM match_lineup_players mlp
JOIN players p
ON p.id = mlp.player_id
WHERE mlp.match_id = %s
ORDER BY
CASE
WHEN LOWER(COALESCE(p.position, '')) IN ('вр', 'вр.', 'gk', 'goalkeeper', 'вратарь') THEN 0
ELSE 1
END,
CASE
WHEN COALESCE(p.number::text, '') ~ '^[0-9]+$' THEN p.number::integer
ELSE 999
END,
COALESCE(p.last_name, ''),
COALESCE(p.first_name, ''),
p.id
""",
(match_id,),
)
player_rows = cur.fetchall()
cur.execute(
"""
SELECT
mc.side,
mc.sort_order,
c.id AS coach_id,
COALESCE(c.player, '') AS coach_name,
COALESCE(mc.role, c.amplua, '') AS role
FROM match_coaches mc
JOIN coaches c
ON c.id = mc.coach_id
WHERE mc.match_id = %s
ORDER BY mc.side, mc.sort_order, mc.id
""",
(match_id,),
)
coach_rows = cur.fetchall()
finally:
conn.close()
result = {
"home_starting": [],
"home_bench": [],
"away_starting": [],
"away_bench": [],
"home_coaches": [],
"away_coaches": [],
}
# 1. Если есть ручной состав — берём его
if player_rows:
for row in player_rows:
player = {
"player_id": row[3],
"player_name": row[4] or "",
"last_name": row[5] or "",
"first_name": row[6] or "",
"number": row[7] or "",
"position": row[8] or "",
"is_captain": bool(row[9]),
"pos": row[10] or "",
}
side = row[0]
role = row[1]
if side == "home" and role == "starting":
result["home_starting"].append(player)
elif side == "home" and role == "bench":
result["home_bench"].append(player)
elif side == "away" and role == "starting":
result["away_starting"].append(player)
elif side == "away" and role == "bench":
result["away_bench"].append(player)
else:
# 2. Иначе — fallback на сайт
lineups = get_match_lineups_grouped(
match_id=match_id,
home_team_id=home_team_id,
away_team_id=away_team_id,
)
result["home_starting"] = lineups.get("home_starting", [])
result["home_bench"] = lineups.get("home_bench", [])
result["away_starting"] = lineups.get("away_starting", [])
result["away_bench"] = lineups.get("away_bench", [])
if coach_rows:
for row in coach_rows:
coach = {
"coach_id": row[2],
"coach_name": row[3] or "",
"role": row[4] or "",
}
if row[0] == "home":
result["home_coaches"].append(coach)
elif row[0] == "away":
result["away_coaches"].append(coach)
else:
coaches = get_match_coaches_grouped(
match_id=match_id,
home_team_id=home_team_id,
away_team_id=away_team_id,
)
result["home_coaches"] = coaches.get("home", [])
result["away_coaches"] = coaches.get("away", [])
return result
def save_match_lineup_for_editor(
match_id: int,
home_team_id: int,
away_team_id: int,
home_starting: list[dict],
home_bench: list[dict],
away_starting: list[dict],
away_bench: list[dict],
home_coaches: list[dict],
away_coaches: list[dict],
) -> None:
def is_goalkeeper(position: str) -> bool:
if not position:
return False
return str(position).strip().lower() in {
"вр", "вр.", "вратарь", "gk", "goalkeeper"
}
def safe_number(value) -> int:
try:
return int(str(value).strip())
except Exception:
return 9999
def lineup_sort_key(player: dict):
return (
0 if is_goalkeeper(player.get("position", "")) else 1,
safe_number(player.get("number")),
(player.get("last_name") or player.get("player_name") or "").lower(),
(player.get("first_name") or "").lower(),
player.get("player_id") or 0,
)
# Пересортировка перед сохранением
home_starting = sorted(home_starting, key=lineup_sort_key)
home_bench = sorted(home_bench, key=lineup_sort_key)
away_starting = sorted(away_starting, key=lineup_sort_key)
away_bench = sorted(away_bench, key=lineup_sort_key)
conn = get_connection()
try:
with conn.cursor() as cur:
cur.execute(
"DELETE FROM match_lineup_players WHERE match_id = %s",
(match_id,),
)
cur.execute(
"DELETE FROM match_coaches WHERE match_id = %s",
(match_id,),
)
def insert_players(side: str, role: str, players: list[dict]) -> None:
for sort_order, p in enumerate(players, start=1):
cur.execute(
"""
INSERT INTO match_lineup_players (
match_id,
side,
role,
sort_order,
player_id,
number,
position,
is_captain
)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
""",
(
match_id,
side,
role,
sort_order,
p.get("player_id"),
p.get("number", "") or None,
p.get("position", "") or None,
bool(p.get("is_captain")),
),
)
def insert_coaches(side: str, coaches: list[dict]) -> None:
for sort_order, c in enumerate(coaches, start=1):
cur.execute(
"""
INSERT INTO match_coaches (
match_id,
side,
sort_order,
coach_id,
role
)
VALUES (%s, %s, %s, %s, %s)
""",
(
match_id,
side,
sort_order,
c.get("coach_id"),
c.get("role", "") or None,
),
)
insert_players("home", "starting", home_starting)
insert_players("home", "bench", home_bench)
insert_players("away", "starting", away_starting)
insert_players("away", "bench", away_bench)
insert_coaches("home", home_coaches)
insert_coaches("away", away_coaches)
conn.commit()
except Exception:
conn.rollback()
raise
finally:
conn.close()