добавил в веб-интерфейс отображение рядом с номером игра фото, если фотографии у игрока нет
This commit is contained in:
2
app.py
2
app.py
@@ -558,6 +558,7 @@ class FormationPlayerPayload(BaseModel):
|
||||
number: str = ""
|
||||
position: str = ""
|
||||
is_captain: bool = False
|
||||
photo_enabled: bool = False
|
||||
x: float
|
||||
y: float
|
||||
|
||||
@@ -575,6 +576,7 @@ class SquadEditorPlayerPayload(BaseModel):
|
||||
number: str = ""
|
||||
position: str = ""
|
||||
is_captain: bool = False
|
||||
photo_enabled: bool = False
|
||||
|
||||
|
||||
class SquadEditorCoachPayload(BaseModel):
|
||||
|
||||
@@ -59,6 +59,7 @@ def _player_to_editor_dict(p: dict) -> dict:
|
||||
"number": str(p.get("number", "") or ""),
|
||||
"position": p.get("position", "") or "",
|
||||
"is_captain": bool(p.get("is_captain")),
|
||||
"photo_enabled": bool(p.get("photo_enabled")),
|
||||
}
|
||||
|
||||
|
||||
@@ -90,7 +91,8 @@ def get_match_lineup_for_editor(
|
||||
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
|
||||
COALESCE(mlp.is_captain, FALSE) AS is_captain,
|
||||
COALESCE(p.photo_enabled, FALSE) AS photo_enabled
|
||||
FROM match_lineup_players mlp
|
||||
JOIN players p
|
||||
ON p.id = mlp.player_id
|
||||
@@ -141,6 +143,7 @@ def get_match_lineup_for_editor(
|
||||
"number": row[7] or "",
|
||||
"position": row[8] or "",
|
||||
"is_captain": bool(row[9]),
|
||||
"photo_enabled": bool(row[10]),
|
||||
}
|
||||
|
||||
side = row[0]
|
||||
@@ -232,11 +235,24 @@ def get_match_lineup_for_vmix(
|
||||
COALESCE(mlp.is_captain, FALSE) AS is_captain,
|
||||
COALESCE(p.position, '') AS pos,
|
||||
p.photo,
|
||||
COALESCE(p.photo_enabled, FALSE) AS photo_enabled,
|
||||
CASE WHEN mlp.side = 'home' THEN %s ELSE %s END AS team_id
|
||||
COALESCE(p.photo_enabled, FALSE) AS photo_enabled
|
||||
FROM match_lineup_players mlp
|
||||
LEFT JOIN players p
|
||||
ON p.id = mlp.player_id
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT p.*
|
||||
FROM players p
|
||||
WHERE p.team_id = CASE WHEN mlp.side = 'home' THEN %s ELSE %s END
|
||||
AND (
|
||||
p.id = mlp.player_id
|
||||
OR (
|
||||
COALESCE(p.number::text, '') <> ''
|
||||
AND COALESCE(p.number::text, '') = COALESCE(mlp.number::text, '')
|
||||
)
|
||||
)
|
||||
ORDER BY
|
||||
CASE WHEN p.id = mlp.player_id THEN 0 ELSE 1 END,
|
||||
p.id
|
||||
LIMIT 1
|
||||
) p ON TRUE
|
||||
WHERE mlp.match_id = %s
|
||||
ORDER BY
|
||||
CASE
|
||||
@@ -298,7 +314,7 @@ def get_match_lineup_for_vmix(
|
||||
"pos": row[10] or "",
|
||||
"photo": row[11] or "",
|
||||
"photo_enabled": bool(row[12]),
|
||||
"team_id": row[13],
|
||||
"team_id": home_team_id if row[0] == "home" else away_team_id,
|
||||
}
|
||||
|
||||
side = row[0]
|
||||
|
||||
@@ -22,7 +22,8 @@ def get_team_players_for_match_editor(
|
||||
COALESCE(p.first_name, '') AS first_name,
|
||||
COALESCE(p.number::text, '') AS number,
|
||||
COALESCE(p.position, '') AS position,
|
||||
FALSE AS is_captain
|
||||
FALSE AS is_captain,
|
||||
COALESCE(p.photo_enabled, FALSE) AS photo_enabled
|
||||
FROM players p
|
||||
WHERE p.team_id = %s
|
||||
ORDER BY
|
||||
@@ -51,6 +52,7 @@ def get_team_players_for_match_editor(
|
||||
"number": row[4] or "",
|
||||
"position": row[5] or "",
|
||||
"is_captain": bool(row[6]),
|
||||
"photo_enabled": bool(row[7]),
|
||||
}
|
||||
for row in rows
|
||||
]
|
||||
|
||||
@@ -469,7 +469,48 @@ def get_vmix_team_formations(session_token: str, team_id: int):
|
||||
mf.team_id,
|
||||
COALESCE(mf.player_name, '') AS player_name
|
||||
FROM match_formations mf
|
||||
LEFT JOIN players p ON p.id = mf.player_id
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT p.*
|
||||
FROM players p
|
||||
WHERE p.team_id = mf.team_id
|
||||
AND (
|
||||
p.id = mf.player_id
|
||||
OR (
|
||||
COALESCE(p.number::text, '') <> ''
|
||||
AND COALESCE(p.number::text, '') = COALESCE(mf.number, '')
|
||||
)
|
||||
OR (
|
||||
COALESCE(mf.player_name, '') <> ''
|
||||
AND (
|
||||
LOWER(COALESCE(p.full_name, '')) = LOWER(TRIM(mf.player_name))
|
||||
OR LOWER(TRIM(COALESCE(p.last_name, '') || ' ' || COALESCE(p.first_name, ''))) = LOWER(TRIM(mf.player_name))
|
||||
OR LOWER(TRIM(COALESCE(p.first_name, '') || ' ' || COALESCE(p.last_name, ''))) = LOWER(TRIM(mf.player_name))
|
||||
)
|
||||
)
|
||||
)
|
||||
ORDER BY
|
||||
CASE
|
||||
WHEN p.id = mf.player_id
|
||||
AND (
|
||||
COALESCE(mf.number, '') = ''
|
||||
OR COALESCE(p.number::text, '') = COALESCE(mf.number, '')
|
||||
OR LOWER(COALESCE(p.full_name, '')) = LOWER(TRIM(COALESCE(mf.player_name, '')))
|
||||
OR LOWER(TRIM(COALESCE(p.last_name, '') || ' ' || COALESCE(p.first_name, ''))) = LOWER(TRIM(COALESCE(mf.player_name, '')))
|
||||
OR LOWER(TRIM(COALESCE(p.first_name, '') || ' ' || COALESCE(p.last_name, ''))) = LOWER(TRIM(COALESCE(mf.player_name, '')))
|
||||
)
|
||||
THEN 0
|
||||
WHEN COALESCE(p.number::text, '') = COALESCE(mf.number, '')
|
||||
AND COALESCE(mf.player_name, '') <> ''
|
||||
THEN 1
|
||||
WHEN COALESCE(mf.player_name, '') <> ''
|
||||
THEN 2
|
||||
WHEN COALESCE(p.number::text, '') = COALESCE(mf.number, '')
|
||||
THEN 3
|
||||
ELSE 4
|
||||
END,
|
||||
p.id
|
||||
LIMIT 1
|
||||
) p ON TRUE
|
||||
WHERE mf.match_id = %s and mf.team_id = %s
|
||||
ORDER BY mf.id
|
||||
"""
|
||||
|
||||
@@ -2277,6 +2277,7 @@ function getRowPlayer(row) {
|
||||
number: row.dataset.playerNumber || "",
|
||||
position: row.dataset.playerPosition || "",
|
||||
isCaptain: row.dataset.playerCaptain === "1",
|
||||
photoEnabled: row.dataset.playerPhotoEnabled === "1",
|
||||
disciplineType: row.dataset.disciplineType || ""
|
||||
};
|
||||
}
|
||||
@@ -2289,6 +2290,7 @@ function setRowPlayer(row, player) {
|
||||
row.dataset.playerNumber = player.number ?? "";
|
||||
row.dataset.playerPosition = player.position ?? "";
|
||||
row.dataset.playerCaptain = player.isCaptain ? "1" : "0";
|
||||
row.dataset.playerPhotoEnabled = player.photoEnabled ? "1" : "0";
|
||||
row.dataset.disciplineType = player.disciplineType ?? "";
|
||||
}
|
||||
|
||||
@@ -2380,6 +2382,9 @@ function renderLineupRow(row) {
|
||||
|
||||
const idJs = JSON.stringify(player.id || player.number || "");
|
||||
const nameJs = JSON.stringify(displayName);
|
||||
const noPhotoIconHtml = player.photoEnabled
|
||||
? ''
|
||||
: '<span class="no-photo-icon" title="Фото выключено: в JSON будет подставлена EMPTY.png" aria-label="Фото выключено">📷</span>';
|
||||
|
||||
let actionsHtml = '';
|
||||
if (role === "starting") {
|
||||
@@ -2411,7 +2416,12 @@ function renderLineupRow(row) {
|
||||
}
|
||||
|
||||
row.innerHTML = `
|
||||
<td class="num-cell">${escapeHtml(player.number || "")}</td>
|
||||
<td class="num-cell">
|
||||
<span class="num-with-photo-status">
|
||||
${noPhotoIconHtml}
|
||||
<span class="player-number-value">${escapeHtml(player.number || "")}</span>
|
||||
</span>
|
||||
</td>
|
||||
<td class="player-cell" title="Показать титр игрока">
|
||||
<button
|
||||
type="button"
|
||||
@@ -3264,7 +3274,8 @@ function normalizePlayer(item = {}) {
|
||||
first_name: item.first_name || "",
|
||||
number: item.number || "",
|
||||
position: item.position || "",
|
||||
is_captain: !!item.is_captain
|
||||
is_captain: !!item.is_captain,
|
||||
photo_enabled: item.photo_enabled === true || item.photo_enabled === 1 || item.photo_enabled === "1"
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3651,6 +3662,7 @@ function rerenderLineupTable(side, role) {
|
||||
number: player.number,
|
||||
position: player.position,
|
||||
isCaptain: !!player.is_captain,
|
||||
photoEnabled: !!player.photo_enabled,
|
||||
disciplineType: ""
|
||||
});
|
||||
renderLineupRow(row);
|
||||
@@ -3680,6 +3692,7 @@ function rebuildFormationFromMatchData(side) {
|
||||
position: p.position || "",
|
||||
is_captain: !!p.is_captain,
|
||||
photo: p.photo || "",
|
||||
photo_enabled: p.photo_enabled === true || p.photo_enabled === 1 || p.photo_enabled === "1",
|
||||
x: old?.x ?? 50,
|
||||
y: old?.y ?? (12 + idx * 6)
|
||||
};
|
||||
|
||||
@@ -2489,3 +2489,29 @@ body:not(.edit-mode-on) .referee-search {
|
||||
.btn-danger:hover {
|
||||
background: #e14b5b;
|
||||
}
|
||||
.num-with-photo-status {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 5px;
|
||||
min-width: 44px;
|
||||
}
|
||||
|
||||
.no-photo-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
opacity: 0.65;
|
||||
filter: grayscale(1);
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.player-number-value {
|
||||
display: inline-block;
|
||||
min-width: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@@ -402,8 +402,16 @@
|
||||
data-player-number="{{ p.number or '' }}"
|
||||
data-player-position="{{ p.position or '' }}"
|
||||
data-player-captain="{{ 1 if p.is_captain else 0 }}"
|
||||
data-player-photo-enabled="{{ 1 if p.photo_enabled else 0 }}"
|
||||
>
|
||||
<td class="num-cell">{{ p.number }}</td>
|
||||
<td class="num-cell">
|
||||
<span class="num-with-photo-status">
|
||||
{% if not p.photo_enabled %}
|
||||
<span class="no-photo-icon" title="Фото выключено: в JSON будет подставлена EMPTY.png" aria-label="Фото выключено">📷</span>
|
||||
{% endif %}
|
||||
<span class="player-number-value">{{ p.number }}</span>
|
||||
</span>
|
||||
</td>
|
||||
<td class="player-cell" title="Показать титр игрока">
|
||||
<div
|
||||
class="player-inline vmix-person-trigger player-link"
|
||||
@@ -510,8 +518,16 @@ data-role="{{ p.position or '' }}"
|
||||
data-player-number="{{ p.number or '' }}"
|
||||
data-player-position="{{ p.position or '' }}"
|
||||
data-player-captain="{{ 1 if p.is_captain else 0 }}"
|
||||
data-player-photo-enabled="{{ 1 if p.photo_enabled else 0 }}"
|
||||
>
|
||||
<td class="num-cell">{{ p.number }}</td>
|
||||
<td class="num-cell">
|
||||
<span class="num-with-photo-status">
|
||||
{% if not p.photo_enabled %}
|
||||
<span class="no-photo-icon" title="Фото выключено: в JSON будет подставлена EMPTY.png" aria-label="Фото выключено">📷</span>
|
||||
{% endif %}
|
||||
<span class="player-number-value">{{ p.number }}</span>
|
||||
</span>
|
||||
</td>
|
||||
<td class="player-cell" title="Показать титр игрока">
|
||||
<div
|
||||
class="player-inline vmix-person-trigger player-link"
|
||||
@@ -628,8 +644,16 @@ data-role="{{ p.position or '' }}"
|
||||
data-player-number="{{ p.number or '' }}"
|
||||
data-player-position="{{ p.position or '' }}"
|
||||
data-player-captain="{{ 1 if p.is_captain else 0 }}"
|
||||
data-player-photo-enabled="{{ 1 if p.photo_enabled else 0 }}"
|
||||
>
|
||||
<td class="num-cell">{{ p.number }}</td>
|
||||
<td class="num-cell">
|
||||
<span class="num-with-photo-status">
|
||||
{% if not p.photo_enabled %}
|
||||
<span class="no-photo-icon" title="Фото выключено: в JSON будет подставлена EMPTY.png" aria-label="Фото выключено">📷</span>
|
||||
{% endif %}
|
||||
<span class="player-number-value">{{ p.number }}</span>
|
||||
</span>
|
||||
</td>
|
||||
<td class="player-cell" title="Показать титр игрока">
|
||||
<div
|
||||
class="player-inline vmix-person-trigger player-link"
|
||||
@@ -736,8 +760,16 @@ data-role="{{ p.position or '' }}"
|
||||
data-player-number="{{ p.number or '' }}"
|
||||
data-player-position="{{ p.position or '' }}"
|
||||
data-player-captain="{{ 1 if p.is_captain else 0 }}"
|
||||
data-player-photo-enabled="{{ 1 if p.photo_enabled else 0 }}"
|
||||
>
|
||||
<td class="num-cell">{{ p.number }}</td>
|
||||
<td class="num-cell">
|
||||
<span class="num-with-photo-status">
|
||||
{% if not p.photo_enabled %}
|
||||
<span class="no-photo-icon" title="Фото выключено: в JSON будет подставлена EMPTY.png" aria-label="Фото выключено">📷</span>
|
||||
{% endif %}
|
||||
<span class="player-number-value">{{ p.number }}</span>
|
||||
</span>
|
||||
</td>
|
||||
<td class="player-cell" title="Показать титр игрока">
|
||||
<div
|
||||
class="player-inline vmix-person-trigger player-link"
|
||||
|
||||
Reference in New Issue
Block a user