browse MV db: routes/player.py
Browse files- routes/player.py +35 -25
routes/player.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
"""GET /api/player/{player_id}?season=... — full detail
|
| 2 |
|
| 3 |
import json
|
| 4 |
import math
|
|
@@ -10,11 +10,19 @@ from data import get_player
|
|
| 10 |
|
| 11 |
router = APIRouter()
|
| 12 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
def _clean(v):
|
| 15 |
-
if v is None:
|
| 16 |
-
return None
|
| 17 |
-
if isinstance(v, float) and (math.isnan(v) or pd.isna(v)):
|
| 18 |
return None
|
| 19 |
if isinstance(v, (pd.Timestamp,)):
|
| 20 |
return v.isoformat()
|
|
@@ -26,17 +34,7 @@ def player_detail(player_id: str, season: str):
|
|
| 26 |
res = get_player(player_id, season)
|
| 27 |
if not res:
|
| 28 |
raise HTTPException(404, f'player {player_id} season {season} not found')
|
| 29 |
-
|
| 30 |
row = res['row']
|
| 31 |
-
blocks = {k.replace('block_', ''): float(v) for k, v in row.items()
|
| 32 |
-
if k.startswith('block_') and pd.notna(v)}
|
| 33 |
-
|
| 34 |
-
cm = row.get('comp_minutes')
|
| 35 |
-
if isinstance(cm, str) and cm.strip().startswith('{'):
|
| 36 |
-
try:
|
| 37 |
-
cm = json.loads(cm)
|
| 38 |
-
except Exception:
|
| 39 |
-
cm = None
|
| 40 |
|
| 41 |
inj = row.get('tm_injuries')
|
| 42 |
if isinstance(inj, str) and inj.strip().startswith('['):
|
|
@@ -47,6 +45,17 @@ def player_detail(player_id: str, season: str):
|
|
| 47 |
elif not isinstance(inj, list):
|
| 48 |
inj = None
|
| 49 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
return {
|
| 51 |
'playerId': str(row['playerId']),
|
| 52 |
'season': row.get('Temporada'),
|
|
@@ -56,18 +65,19 @@ def player_detail(player_id: str, season: str):
|
|
| 56 |
'pos': row.get('Posicion'),
|
| 57 |
'minutos': int(row.get('Minutos totales') or 0),
|
| 58 |
'partidos': int(row.get('Partidos jugados') or 0),
|
| 59 |
-
'
|
| 60 |
-
'
|
| 61 |
-
'
|
| 62 |
-
'
|
| 63 |
-
'
|
| 64 |
-
'
|
| 65 |
-
'
|
| 66 |
-
'
|
| 67 |
-
'
|
|
|
|
| 68 |
'tm_id': _clean(row.get('tm_id')),
|
| 69 |
'tm_url': _clean(row.get('tm_url')),
|
| 70 |
-
'
|
| 71 |
-
'history': res['history'],
|
| 72 |
'injuries': inj,
|
|
|
|
| 73 |
}
|
|
|
|
| 1 |
+
"""GET /api/player/{player_id}?season=... — full detail for the browse drawer."""
|
| 2 |
|
| 3 |
import json
|
| 4 |
import math
|
|
|
|
| 10 |
|
| 11 |
router = APIRouter()
|
| 12 |
|
| 13 |
+
# identity / meta columns excluded from the generic "stats" grid
|
| 14 |
+
META_COLS = {
|
| 15 |
+
'playerId', 'tm_id', 'Jugador', 'Temporada', 'nationality', 'age', 'date_of_birth',
|
| 16 |
+
'Equipo', 'Competencia', 'Posicion', 'Posiciones', 'Estado', 'teamId', 'comps_list',
|
| 17 |
+
'comp_minutes', 'n_competitions', 'Minutos totales', 'Partidos jugados',
|
| 18 |
+
'mv_eos', 'mv_eos_prev', 'tm_mv_now', 'mv_source', 'match_path', 'match_conf',
|
| 19 |
+
'tm_position', 'tm_foot', 'tm_height', 'tm_contract_until', 'tm_url', 'tm_injuries',
|
| 20 |
+
'n_opta_ids',
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
|
| 24 |
def _clean(v):
|
| 25 |
+
if v is None or (isinstance(v, float) and (math.isnan(v) or pd.isna(v))):
|
|
|
|
|
|
|
| 26 |
return None
|
| 27 |
if isinstance(v, (pd.Timestamp,)):
|
| 28 |
return v.isoformat()
|
|
|
|
| 34 |
res = get_player(player_id, season)
|
| 35 |
if not res:
|
| 36 |
raise HTTPException(404, f'player {player_id} season {season} not found')
|
|
|
|
| 37 |
row = res['row']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
|
| 39 |
inj = row.get('tm_injuries')
|
| 40 |
if isinstance(inj, str) and inj.strip().startswith('['):
|
|
|
|
| 45 |
elif not isinstance(inj, list):
|
| 46 |
inj = None
|
| 47 |
|
| 48 |
+
history = [{k: _clean(v) for k, v in h.items()} for h in res['history']]
|
| 49 |
+
|
| 50 |
+
# all remaining numeric stat columns, non-null
|
| 51 |
+
stats = {}
|
| 52 |
+
for k, v in row.items():
|
| 53 |
+
if k in META_COLS:
|
| 54 |
+
continue
|
| 55 |
+
cv = _clean(v)
|
| 56 |
+
if cv is not None and isinstance(cv, (int, float)):
|
| 57 |
+
stats[k] = round(float(cv), 3)
|
| 58 |
+
|
| 59 |
return {
|
| 60 |
'playerId': str(row['playerId']),
|
| 61 |
'season': row.get('Temporada'),
|
|
|
|
| 65 |
'pos': row.get('Posicion'),
|
| 66 |
'minutos': int(row.get('Minutos totales') or 0),
|
| 67 |
'partidos': int(row.get('Partidos jugados') or 0),
|
| 68 |
+
'age': _clean(row.get('age')),
|
| 69 |
+
'nationality': _clean(row.get('nationality')),
|
| 70 |
+
'foot': _clean(row.get('tm_foot')),
|
| 71 |
+
'height': _clean(row.get('tm_height')),
|
| 72 |
+
'mv_eos': _clean(row.get('mv_eos')),
|
| 73 |
+
'mv_eos_prev': _clean(row.get('mv_eos_prev')),
|
| 74 |
+
'mv_now': _clean(row.get('tm_mv_now')),
|
| 75 |
+
'mv_source': _clean(row.get('mv_source')),
|
| 76 |
+
'match_conf': _clean(row.get('match_conf')),
|
| 77 |
+
'contract': _clean(row.get('tm_contract_until')),
|
| 78 |
'tm_id': _clean(row.get('tm_id')),
|
| 79 |
'tm_url': _clean(row.get('tm_url')),
|
| 80 |
+
'history': history,
|
|
|
|
| 81 |
'injuries': inj,
|
| 82 |
+
'stats': stats,
|
| 83 |
}
|