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 clean_time(t: str) -> str:
"""Remove ESPN artifacts like 'v', 'v2nd Leg' etc from time field."""
t = t.strip()
# If it looks like a time (contains :) keep it, otherwise TBD
if re.search(r'\d+:\d+', t):
return re.search(r'\d+:\d+\s*(?:AM|PM)?', t).group(0).strip()
return "TBD"
def shorten(name: str) -> str:
if len(name) <= 10:
return name
parts = name.split()
return parts[-1] if parts else name[:10]
def card(m: dict, is_past: bool) -> str: def card(m: dict, is_past: bool) -> str:
home = m.get("home", "")
away = m.get("away", "")
home_logo = m.get("home_logo") or PORTO_LOGO home_logo = m.get("home_logo") or PORTO_LOGO
away_logo = m.get("away_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", "") abbr = m.get("abbr", "")
score = m.get("score") score = m.get("score")
date = m.get("date", "") date = m.get("date", "")
time_ = m.get("time", "") time_raw = 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:
if len(name) <= 10:
return name
parts = name.split()
return parts[-1] if parts else name[:10]
home_s = shorten(home)
away_s = shorten(away)
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>'
cls = "card future"
time_s = f"<br>{time_}" if time_ and time_ != "TBD" else "" return f"""<div class="{cls}">
<img src="{home_logo}" class="badge" alt="">
cls = "card future"
return f"""
<div class="{cls}">
<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;