Files
sentiment-engine/sentiment_engine/prefect_flows/connectors/api_ingest.py

101 lines
2.9 KiB
Python

"""API ingestion Prefect flow"""
import asyncio
import logging
from typing import Dict, List, Optional
import aiohttp
from prefect import flow, task
from prefect.task_runners import ConcurrentTaskRunner
from sentiment_engine.utils.config import get_settings
logger = logging.getLogger(__name__)
@task(retries=2, retry_delay_seconds=60)
async def fetch_api_endpoint(
url: str,
headers: Dict[str, str] = None,
params: Dict = None
) -> List[dict]:
"""Fetch a single API endpoint"""
try:
async with aiohttp.ClientSession() as session:
async with session.get(url, headers=headers, params=params, timeout=30) as resp:
if resp.status != 200:
logger.warning(f"API {url} returned {resp.status}")
return []
data = await resp.json()
# Normalize to list of items
items = data if isinstance(data, list) else [data]
return items
except Exception as e:
logger.error(f"Error fetching {url}: {e}")
raise
@flow(
name="api_ingest",
task_runner=ConcurrentTaskRunner(max_workers=5),
log_prints=True
)
async def api_ingest_flow():
"""Main API ingestion flow for FRED, EDGAR, etc."""
settings = get_settings()
endpoints = [
{
"name": "fred_vix",
"url": "https://api.stlouisfed.org/fred/series/observations",
"params": {
"series_id": "VIXCLS",
"api_key": "${FRED_API_KEY}",
"file_type": "json",
"limit": 1,
"sort_order": "desc"
},
"source_type": "regulatory"
},
{
"name": "fred_dxy",
"url": "https://api.stlouisfed.org/fred/series/observations",
"params": {
"series_id": "DTWEXBGS",
"api_key": "${FRED_API_KEY}",
"file_type": "json",
"limit": 1,
"sort_order": "desc"
},
"source_type": "regulatory"
},
# Add more FRED series, EDGAR, etc.
]
results = await asyncio.gather(
*[fetch_api_endpoint(ep["url"], params=ep.get("params")) for ep in endpoints],
return_exceptions=True
)
all_items = []
for i, result in enumerate(results):
ep = endpoints[i]
if isinstance(result, Exception):
logger.error(f"Endpoint {ep['name']} failed: {result}")
else:
for item in result:
all_items.append({
"source_id": f"api:{ep['name']}",
"source_type": ep["source_type"],
"raw_text": str(item),
"metadata": {"endpoint": ep["name"], "raw": item}
})
logger.info(f"Fetched {len(all_items)} items from {len(endpoints)} API endpoints")
return all_items
if __name__ == "__main__":
asyncio.run(api_ingest_flow())