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