amplegest / ingestion /transcript.py
Viney's picture
chore: checkpoint sidebar nav + i18n work-in-progress before Decision Stack redesign
e6496c0
Raw
History Blame Contribute Delete
3.53 kB
import logging
import httpx
from ingestion.alphavantage import AV_BASE, _next_key, _key_count, _is_rate_limited
logger = logging.getLogger(__name__)
def _parse_transcript(data: dict) -> str:
raw = data.get("transcript", "")
if isinstance(raw, list):
lines = []
for item in raw:
speaker = item.get("speaker", "")
title = item.get("title", "")
label = f"{speaker} ({title})" if title else speaker
lines.append(f"{label}: {item.get('content', '')}")
return "\n".join(lines)
if raw is None:
return ""
return str(raw)
def fetch_transcript(ticker: str, quarter: str) -> str:
"""Fetch earnings call transcript from Alpha Vantage.
Args:
ticker: Stock symbol e.g. "AAPL"
quarter: Alpha Vantage format e.g. "2024Q4"
Returns:
Transcript text, or empty string if unavailable.
"""
rate_limited_keys = 0
n_keys = max(_key_count(), 1)
for attempt in range(n_keys):
key = _next_key()
key_label = f"key[...{key[-4:]}]"
try:
resp = httpx.get(
AV_BASE,
params={
"function": "EARNINGS_CALL_TRANSCRIPT",
"symbol": ticker.upper(),
"quarter": quarter,
"apikey": key,
},
timeout=30,
)
resp.raise_for_status()
data = resp.json()
if "Information" in data:
rate_limited_keys += 1
logger.warning(
"[transcript] %s %s %s β€” rate limit hit (attempt %d/%d): %s",
ticker, quarter, key_label, attempt + 1, n_keys, data["Information"],
)
continue
if "Note" in data:
rate_limited_keys += 1
logger.warning(
"[transcript] %s %s %s β€” API note/rate limit (attempt %d/%d): %s",
ticker, quarter, key_label, attempt + 1, n_keys, data["Note"],
)
continue
if "Error Message" in data:
logger.warning(
"[transcript] %s %s %s β€” API error: %s",
ticker, quarter, key_label, data["Error Message"],
)
return ""
text = _parse_transcript(data)
if text:
logger.info(
"[transcript] %s %s β€” fetched %d chars via %s",
ticker, quarter, len(text), key_label,
)
else:
logger.info(
"[transcript] %s %s β€” API returned empty transcript (no content available) via %s",
ticker, quarter, key_label,
)
return text
except ValueError:
raise
except Exception as e:
logger.warning(
"[transcript] %s %s %s β€” request error (attempt %d/%d): %s",
ticker, quarter, key_label, attempt + 1, n_keys, e,
)
continue
if rate_limited_keys == n_keys:
logger.error(
"[transcript] %s %s β€” ALL %d keys exhausted by rate limits. No transcript fetched.",
ticker, quarter, n_keys,
)
else:
logger.error(
"[transcript] %s %s β€” failed after %d attempt(s). No transcript fetched.",
ticker, quarter, n_keys,
)
return ""