fix: update UCDP GED API to v25.1 + add token auth support

UCDP now requires x-ucdp-access-token header (previously open access).
Updated from v24.1 to v25.1 (latest stable). Token passed via
UCDP_ACCESS_TOKEN env var; gracefully degrades to 0 events without it
(conflict zone fallback uses static INTEL_HOTSPOTS).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Marc Shade
2026-02-24 11:36:26 -05:00
co-authored by Claude Opus 4.6
parent 0775102ed3
commit fc105872e2
+11 -3
View File
@@ -203,7 +203,7 @@ async def fetch_acled_events(
# UCDP: Uppsala Conflict Data Program (GED API) # UCDP: Uppsala Conflict Data Program (GED API)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
_UCDP_GED_URL = "https://ucdpapi.pcr.uu.se/api/gedevents/24.1" _UCDP_GED_URL = "https://ucdpapi.pcr.uu.se/api/gedevents/25.1"
_VIOLENCE_TYPES = { _VIOLENCE_TYPES = {
1: "state-based", 1: "state-based",
@@ -219,8 +219,9 @@ async def fetch_ucdp_events(
) -> dict: ) -> dict:
"""Fetch recent conflict events from the UCDP GED API. """Fetch recent conflict events from the UCDP GED API.
No API key required. Fetches multiple pages in parallel when the Optionally uses ``UCDP_ACCESS_TOKEN`` if set (required since 2026).
dataset spans more than one page. Fetches multiple pages in parallel when the dataset spans more than
one page.
Args: Args:
fetcher: Shared HTTP fetcher with caching and circuit breaking. fetcher: Shared HTTP fetcher with caching and circuit breaking.
@@ -235,6 +236,11 @@ async def fetch_ucdp_events(
now = datetime.now(timezone.utc) now = datetime.now(timezone.utc)
cutoff = now - timedelta(days=days) cutoff = now - timedelta(days=days)
ucdp_headers: dict[str, str] = {}
ucdp_token = os.environ.get("UCDP_ACCESS_TOKEN")
if ucdp_token:
ucdp_headers["x-ucdp-access-token"] = ucdp_token
# Fetch page 1 to discover total pages (UCDP is slow — 30s timeout) # Fetch page 1 to discover total pages (UCDP is slow — 30s timeout)
page1_data = await fetcher.get_json( page1_data = await fetcher.get_json(
_UCDP_GED_URL, _UCDP_GED_URL,
@@ -242,6 +248,7 @@ async def fetch_ucdp_events(
cache_key=f"conflict:ucdp:{days}:page1", cache_key=f"conflict:ucdp:{days}:page1",
cache_ttl=21600, cache_ttl=21600,
params={"pagesize": min(limit, 1000), "page": 0}, params={"pagesize": min(limit, 1000), "page": 0},
headers=ucdp_headers or None,
timeout=30.0, timeout=30.0,
) )
@@ -268,6 +275,7 @@ async def fetch_ucdp_events(
cache_key=f"conflict:ucdp:{days}:page{p}", cache_key=f"conflict:ucdp:{days}:page{p}",
cache_ttl=21600, cache_ttl=21600,
params={"pagesize": min(limit, 1000), "page": p}, params={"pagesize": min(limit, 1000), "page": p},
headers=ucdp_headers or None,
timeout=30.0, timeout=30.0,
) )
for p in range(1, total_pages) for p in range(1, total_pages)