Syntrex Claude Sonnet 4.6 commited on
Commit
07e8342
·
1 Parent(s): 999cacf

Add forensic diagnostic logging to fetch_all_upcoming_hr_props

Browse files

Instruments the per-event odds path with full visibility into every step:
- Step 1: log exact events request params (apiKey masked)
- Step 2: log exact per-event odds params + HTTP status before raise_for_status
- Response: log type, top-level keys, books returned, markets per book, outcome counts
- Row-build loop: counters for bookmakers/markets/outcomes/rows plus per-reason skip counts
- Sample first 3 outcomes verbatim (keys, name, description, price, point)

No fixes applied. Diagnostic only — break point to be identified from log output.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Files changed (1) hide show
  1. data/provider_theoddsapi.py +73 -3
data/provider_theoddsapi.py CHANGED
@@ -221,7 +221,11 @@ class TheOddsAPIProvider(MarketProviderBase):
221
  "commenceTimeTo": (now + timedelta(days=7)).strftime("%Y-%m-%dT%H:%M:%SZ"),
222
  }
223
 
224
- _diag_log.info("[upcoming_hr_props] Step1 events window=now+7d")
 
 
 
 
225
  try:
226
  r1 = requests.get(events_url, params=events_params, timeout=30)
227
  _diag_log.info(
@@ -262,8 +266,24 @@ class TheOddsAPIProvider(MarketProviderBase):
262
  "oddsFormat": "american",
263
  "dateFormat": "iso",
264
  }
 
 
 
 
 
 
 
 
265
  try:
266
  r2 = requests.get(odds_url, params=odds_params, timeout=30)
 
 
 
 
 
 
 
 
267
  r2.raise_for_status()
268
  except (requests.HTTPError, requests.RequestException) as exc:
269
  _diag_log.warning(
@@ -276,30 +296,71 @@ class TheOddsAPIProvider(MarketProviderBase):
276
  bookmakers = (
277
  event_data.get("bookmakers", []) if isinstance(event_data, dict) else []
278
  )
 
 
 
 
 
 
 
 
 
 
 
 
279
  _diag_log.info(
280
- "[upcoming_hr_props] %s@%s books=%s",
281
- away_team, home_team, [b.get("key") for b in bookmakers],
 
282
  )
283
 
 
 
 
 
 
 
 
 
 
284
  for bookmaker in bookmakers:
 
285
  book_key = str(bookmaker.get("key", "") or "")
286
  book_name = BOOK_KEY_MAP.get(book_key, book_key)
287
 
288
  for market in bookmaker.get("markets", []) or []:
 
289
  market_key = str(market.get("key", "") or "")
290
  if market_key != "batter_home_runs":
 
291
  continue
292
  market_name = MARKET_NAME_MAP.get(market_key, market_key)
293
 
294
  for outcome in market.get("outcomes", []) or []:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
295
  player_name_raw = str(
296
  outcome.get("description", "") or outcome.get("name", "") or ""
297
  ).strip()
298
  if not player_name_raw:
 
299
  continue
300
 
301
  price = outcome.get("price")
302
  if price is None:
 
303
  continue
304
 
305
  rows.append(
@@ -319,6 +380,15 @@ class TheOddsAPIProvider(MarketProviderBase):
319
  "line": _safe_float(outcome.get("point")),
320
  }
321
  )
 
 
 
 
 
 
 
 
 
322
 
323
  _diag_log.info("[upcoming_hr_props] total rows=%d", len(rows))
324
  return pd.DataFrame(rows)
 
221
  "commenceTimeTo": (now + timedelta(days=7)).strftime("%Y-%m-%dT%H:%M:%SZ"),
222
  }
223
 
224
+ _diag_log.info(
225
+ "[upcoming_hr_props] Step1 GET %s params=%s",
226
+ events_url,
227
+ {k: (v if k != "apiKey" else v[:6] + "...") for k, v in events_params.items()},
228
+ )
229
  try:
230
  r1 = requests.get(events_url, params=events_params, timeout=30)
231
  _diag_log.info(
 
266
  "oddsFormat": "american",
267
  "dateFormat": "iso",
268
  }
269
+ _diag_log.info(
270
+ "[upcoming_hr_props] Step2 event_id=%s %s@%s url=%s params=%s",
271
+ event_id,
272
+ away_team,
273
+ home_team,
274
+ odds_url,
275
+ {k: (v if k != "apiKey" else v[:6] + "...") for k, v in odds_params.items()},
276
+ )
277
  try:
278
  r2 = requests.get(odds_url, params=odds_params, timeout=30)
279
+ _diag_log.info(
280
+ "[upcoming_hr_props] Step2 HTTP %s | remaining=%s | event_id=%s %s@%s",
281
+ r2.status_code,
282
+ r2.headers.get("x-requests-remaining", "?"),
283
+ event_id,
284
+ away_team,
285
+ home_team,
286
+ )
287
  r2.raise_for_status()
288
  except (requests.HTTPError, requests.RequestException) as exc:
289
  _diag_log.warning(
 
296
  bookmakers = (
297
  event_data.get("bookmakers", []) if isinstance(event_data, dict) else []
298
  )
299
+ _data_type = type(event_data).__name__
300
+ _top_keys = list(event_data.keys()) if isinstance(event_data, dict) else f"LIST len={len(event_data)}"
301
+ _bk_keys = [b.get("key") for b in bookmakers]
302
+ _markets_by_book = {
303
+ b.get("key"): [m.get("key") for m in b.get("markets", [])]
304
+ for b in bookmakers
305
+ }
306
+ _outcome_counts = {
307
+ f"{b.get('key')}:{m.get('key')}": len(m.get("outcomes", []))
308
+ for b in bookmakers
309
+ for m in b.get("markets", [])
310
+ }
311
  _diag_log.info(
312
+ "[upcoming_hr_props] %s@%s type=%s top_keys=%s books=%s markets=%s outcome_counts=%s",
313
+ away_team, home_team, _data_type, _top_keys, _bk_keys,
314
+ _markets_by_book, _outcome_counts,
315
  )
316
 
317
+ _bookmakers_seen = 0
318
+ _markets_seen = 0
319
+ _outcomes_seen = 0
320
+ _rows_appended = 0
321
+ _skip_market_mismatch = 0
322
+ _skip_empty_name = 0
323
+ _skip_missing_price = 0
324
+ _sample_logged = False
325
+
326
  for bookmaker in bookmakers:
327
+ _bookmakers_seen += 1
328
  book_key = str(bookmaker.get("key", "") or "")
329
  book_name = BOOK_KEY_MAP.get(book_key, book_key)
330
 
331
  for market in bookmaker.get("markets", []) or []:
332
+ _markets_seen += 1
333
  market_key = str(market.get("key", "") or "")
334
  if market_key != "batter_home_runs":
335
+ _skip_market_mismatch += 1
336
  continue
337
  market_name = MARKET_NAME_MAP.get(market_key, market_key)
338
 
339
  for outcome in market.get("outcomes", []) or []:
340
+ _outcomes_seen += 1
341
+
342
+ if not _sample_logged and _outcomes_seen <= 3:
343
+ _diag_log.info(
344
+ "[upcoming_hr_props] sample outcome keys=%s name=%r description=%r price=%r point=%r",
345
+ list(outcome.keys()),
346
+ outcome.get("name"),
347
+ outcome.get("description"),
348
+ outcome.get("price"),
349
+ outcome.get("point"),
350
+ )
351
+ if _outcomes_seen >= 3:
352
+ _sample_logged = True
353
+
354
  player_name_raw = str(
355
  outcome.get("description", "") or outcome.get("name", "") or ""
356
  ).strip()
357
  if not player_name_raw:
358
+ _skip_empty_name += 1
359
  continue
360
 
361
  price = outcome.get("price")
362
  if price is None:
363
+ _skip_missing_price += 1
364
  continue
365
 
366
  rows.append(
 
380
  "line": _safe_float(outcome.get("point")),
381
  }
382
  )
383
+ _rows_appended += 1
384
+
385
+ _diag_log.info(
386
+ "[upcoming_hr_props] %s@%s counters: bookmakers=%d markets=%d outcomes=%d rows=%d "
387
+ "skip_market=%d skip_name=%d skip_price=%d",
388
+ away_team, home_team,
389
+ _bookmakers_seen, _markets_seen, _outcomes_seen, _rows_appended,
390
+ _skip_market_mismatch, _skip_empty_name, _skip_missing_price,
391
+ )
392
 
393
  _diag_log.info("[upcoming_hr_props] total rows=%d", len(rows))
394
  return pd.DataFrame(rows)