Update api/main.py
All checks were successful
Build & Push Football Docker Images / build-push-update (push) Successful in 7s

Clean up
This commit is contained in:
2026-04-12 15:22:32 +00:00
parent 6603142e0b
commit 4509850a29

View File

@@ -4,11 +4,12 @@ FC Porto fixtures API
Endpoints: Endpoints:
GET /health — liveness GET /health — liveness
GET /data — raw JSON GET /data — raw JSON
GET /next — next match (for Homepage customapi) GET /next — next match
GET /widget — self-contained HTML widget for Homepage iframe GET /widget — HTML widget for Homepage iframe
""" """
import json import json
import re
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
from zoneinfo import ZoneInfo from zoneinfo import ZoneInfo
@@ -94,87 +95,66 @@ def next_match():
@app.get("/widget", response_class=HTMLResponse) @app.get("/widget", response_class=HTMLResponse)
def widget(): def widget():
"""
Layout: 3 past results + 2 upcoming fixtures
For each match card:
[ home badge ]
[ home name ]
[ score/date ]
[ away badge ]
[ away name ]
[ comp abbr ]
"""
try: try:
d = load() d = load()
past, future = sorted_matches(d) past, future = sorted_matches(d)
except HTTPException: except HTTPException:
past, future = [], [] past, future = [], []
# Last 3 past + next 2 future # Most recent 3 past (newest leftmost = reversed) + next 2 future
last3 = past[-3:] if len(past) >= 3 else past last3 = list(reversed(past[-3:])) if len(past) >= 3 else list(reversed(past))
next2 = future[:2] if len(future) >= 2 else future next2 = future[:2] if len(future) >= 2 else future
# Pad with empties empty_past = {"home": "", "away": "", "home_logo": None, "away_logo": None,
empty = { "abbr": "", "date": "", "time": "", "score": None}
"home": "", "away": "", empty_future = {**empty_past, "score": None}
"home_logo": None, "away_logo": None,
"competition": "", "abbr": "",
"date": "", "time": "", "score": None, "is_past": True,
}
while len(last3) < 3:
last3.insert(0, dict(empty))
while len(next2) < 2:
next2.append(dict(empty, is_past=False))
all_cards = last3 + next2 while len(last3) < 3:
last3.append(dict(empty_past))
while len(next2) < 2:
next2.append(dict(empty_future))
PORTO_LOGO = "https://a.espncdn.com/i/teamlogos/soccer/500/437.png" PORTO_LOGO = "https://a.espncdn.com/i/teamlogos/soccer/500/437.png"
def card(m: dict, is_past: bool) -> str: def clean_time(t: str) -> str:
home = m.get("home", "") """Remove ESPN artifacts like 'v', 'v2nd Leg' etc from time field."""
away = m.get("away", "") t = t.strip()
home_logo = m.get("home_logo") or PORTO_LOGO # If it looks like a time (contains :) keep it, otherwise TBD
away_logo = m.get("away_logo") or PORTO_LOGO if re.search(r'\d+:\d+', t):
abbr = m.get("abbr", "") return re.search(r'\d+:\d+\s*(?:AM|PM)?', t).group(0).strip()
score = m.get("score") return "TBD"
date = m.get("date", "")
time_ = m.get("time", "")
time_str = m.get("time", "")
time_str = re.sub(r'^v\s*', '', time_str).strip()
time_str = time_str if time_str not in ("", "TBD", "v") else "TBD"
# Shorten names for display
def shorten(name: str) -> str: def shorten(name: str) -> str:
if len(name) <= 10: if len(name) <= 10:
return name return name
parts = name.split() parts = name.split()
return parts[-1] if parts else name[:10] return parts[-1] if parts else name[:10]
home_s = shorten(home) def card(m: dict, is_past: bool) -> str:
away_s = shorten(away) home_logo = m.get("home_logo") or PORTO_LOGO
away_logo = m.get("away_logo") or PORTO_LOGO
home_s = shorten(m.get("home", ""))
away_s = shorten(m.get("away", ""))
abbr = m.get("abbr", "")
score = m.get("score")
date = m.get("date", "")
time_raw = m.get("time", "")
if is_past and score: if is_past and score:
middle = f'<div class="score">{score}</div>' middle = f'<div class="score">{score}</div>'
cls = "card past" cls = "card past"
else: else:
date_s = "/".join(date.split("/")[:2]) if "/" in date else date date_s = "/".join(date.split("/")[:2]) if "/" in date else date
time_part = f"<br>{time_str}" if time_str and time_str != "TBD" else "" time_clean = clean_time(time_raw)
time_part = f"<br>{time_clean}" if time_clean != "TBD" else ""
middle = f'<div class="fixture">{date_s}{time_part}</div>' middle = f'<div class="fixture">{date_s}{time_part}</div>'
time_s = f"<br>{time_}" if time_ and time_ != "TBD" else ""
cls = "card future" cls = "card future"
return f""" return f"""<div class="{cls}">
<div class="{cls}"> <img src="{home_logo}" class="badge" alt="">
<img src="{home_logo}" class="badge" alt="{home}">
<div class="name home-name">{home_s}</div> <div class="name home-name">{home_s}</div>
{middle} {middle}
<img src="{away_logo}" class="badge" alt="{away}"> <img src="{away_logo}" class="badge" alt="">
<div class="name away-name">{away_s}</div> <div class="name away-name">{away_s}</div>
<div class="comp">{abbr}</div> <div class="comp">{abbr}</div>
</div>""" </div>"""
@@ -197,6 +177,12 @@ def widget():
align-items: center; align-items: center;
overflow: hidden; overflow: hidden;
}} }}
.container {{
display: flex;
align-items: center;
width: 100%;
padding: 0 4px;
}}
.section {{ .section {{
display: flex; display: flex;
align-items: stretch; align-items: stretch;
@@ -205,33 +191,26 @@ def widget():
.divider {{ .divider {{
width: 1px; width: 1px;
background: #334155; background: #334155;
margin: 4px 6px; margin: 4px 8px;
align-self: stretch; align-self: stretch;
flex-shrink: 0; flex-shrink: 0;
}} }}
.container {{
display: flex;
align-items: center;
width: 100%;
padding: 0 4px;
}}
.card {{ .card {{
flex: 1; flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 2px 4px; padding: 2px 6px;
gap: 1px; gap: 2px;
min-width: 0; min-width: 60px;
}} }}
.past {{ opacity: 0.7; }} .past {{ opacity: 0.7; }}
.future {{ opacity: 1; }} .future {{ opacity: 1; }}
.badge {{ .badge {{
width: 22px; width: 24px;
height: 22px; height: 24px;
object-fit: contain; object-fit: contain;
flex-shrink: 0;
}} }}
.name {{ .name {{
font-size: 9px; font-size: 9px;
@@ -245,16 +224,16 @@ def widget():
.home-name {{ color: #cbd5e1; }} .home-name {{ color: #cbd5e1; }}
.away-name {{ color: #94a3b8; }} .away-name {{ color: #94a3b8; }}
.score {{ .score {{
font-size: 11px; font-size: 12px;
font-weight: 800; font-weight: 800;
color: #4ade80; color: #4ade80;
padding: 1px 0; padding: 2px 0;
}} }}
.fixture {{ .fixture {{
font-size: 9px; font-size: 9px;
color: #93c5fd; color: #93c5fd;
text-align: center; text-align: center;
line-height: 1.3; line-height: 1.4;
}} }}
.comp {{ .comp {{
font-size: 8px; font-size: 8px;