Distinguish outages from data absence; add failure taxonomy - #1
Merged
Merged
Conversation
Every client now separates three failures that used to look like "no data": an upstream outage (SourceUnavailable), a missing or refused credential (MissingCredential) and a country the source does not cover (NotCovered). fetch_country_data.py records one status per source in fetch_summary.csv and exits 1 when a source failed, so an empty CSV is never mistaken for a finding. Correctness fixes found in review: - INFORM: CC.INS is Institutional and CC.INF Infrastructure (were swapped); a missing score is None, not 0. - ACLED: a country outside the name table raises instead of silently querying the wrong country; CAST filters on an exact country match. - Liveuamap: regional feeds are refused unless allowed; South Sudan is marked as read from the Sudan feed; event category 51 is power infrastructure; timestamps are UTC. - HDX CKAN lists are paginated; HAPI, UNHCR, IFRC GO and DTM check that the returned rows belong to the requested country. - ReliefWeb 403 (unapproved appname) is reported with the fix to apply. - report_figures: confusable-country and subnational guards (Sudan vs South Sudan, Gaza/West Bank), directional change parsing, PDF cache. Safer downloads: http(s)-only redirects that drop credentials across hosts, truncation and HTML-instead-of-file detection, atomic .part writes, sanitised file names; wgss_probe keeps MSNA microdata in a temp directory removed on exit. Tests: test_hardening grows to 71 offline checks; the live suites skip only on a real outage and fail on empty answers. New CI workflow runs the offline suite on Linux and Windows (3.9, 3.12), pyflakes and vermin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SQ5UN2pMiamMm4HGGqKA7j
…ndicators A live Sudan run from 2026-08-01 gave three misleading answers: - HAPI kept a row only when its reference period STARTED in the window, so the 2026 HNO (January to December) was dropped and the summary claimed "no disability disaggregation" while 6,515 disability rows exist for 2024-2025. The IPC projection for June to September 2026, the one that covers the window, was dropped too: only the October projection was kept. Rows are now kept when their period overlaps the window, the rule HAPI's own start_date/end_date filters apply, and every zero says what exists outside it: "IDPs: 0 records (19741 outside the period, covering 2010-06-30 to 2026-06-30)". - UNHCR has no 2026 population figures yet and the summary read "0 population records"; it now says why and how to rerun. - World Bank SM.POP.REFG and SM.POP.REFG.OR moved to the WDI archives and answer "indicator not found", so every profile came out partial. They are removed; refugee figures come from UNHCR. The offline suite gains a period-filter section (78 checks). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SQ5UN2pMiamMm4HGGqKA7j
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR introduces a failure taxonomy to distinguish between provider outages (network failures, HTTP 5xx errors) and legitimate data absence (empty results, missing coverage). Previously, all failures were silently caught and returned empty lists, making outages indistinguishable from "no data for this country," which caused the pipeline to report zero records as if they were facts.
Key Changes
New exception hierarchy in
config.py:SourceUnavailable: Provider unreachable or returned server error (outage)MissingCredential: Free API key not configured (skip, not zero)NotCovered: Source has no coverage for this country (absence of coverage, not absence of data)is_outage(): Classifies exceptions as outages vs. bugs (HTTP 5xx/429, timeouts, connection errors vs. 4xx/schema changes)Pipeline status tracking:
statusfield to fetch results with seven states:ok,empty,partial,unavailable,error,skipped,not_covered_attempt()helper: Runs individual source requests, records failures without swallowing them_state(): Determines final source status from collected errors and record countNone(printed as "n/a") instead of fake zerosOutput improvements:
fetch_summary.csvnow includesstatuscolumn and detailed error notesdata_inventory.csvtracks actual files on disk with row countsSOURCE_FILESregistry: Defines exact filenames each source produces; previous outputs are cleared before each run to prevent stale dataClient-side changes:
SourceUnavailable,MissingCredential, orNotCoveredinstead of returning empty listsraise_unavailable()helper for consistent error handlingget_credential()centralized in config; missing keys raiseMissingCredentialRobustness fixes:
sys.stdout.reconfigure()now guarded withhasattr()check (absent in Jupyter, IDLE, captured output)filter_by_period()keeps records with overlapping date ranges, not just start datesformatparameter renamed toformat_filterto avoid shadowing builtinapp_identifiernow properly URL-encoded (base64 contains unsafe characters)MissingCredentialwith setup instructionsTest suite enhancements:
test_hardening.py: Tests 7–8 verify outage taxonomy and that network failures don't produce fake zerostest_skill_50.py:rec_exc()distinguishes outages (SKIP) from bugs (GAP)test_p0_e2e.py,test_dtm_e2e.py: Updated to useis_outage()for consistent failure classificationDocumentation:
wgss_probe.pynow usestempfile.mkdtemp()instead of hardcoded path (was relative on Linux/macOS)Notable Implementation Details
[date_start, date_end]and requested[date_from, date_to], not justhttps://claude.ai/code/session_01SQ5UN2pMiamMm4HGGqKA7j