agentdata 0.3.26__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agentdata/__init__.py +33 -0
- agentdata/__main__.py +4 -0
- agentdata/cache.py +115 -0
- agentdata/cli.py +173 -0
- agentdata/client.py +158 -0
- agentdata/company/__init__.py +3 -0
- agentdata/company/cf.py +145 -0
- agentdata/company/ch_zefix.py +100 -0
- agentdata/company/cli.py +243 -0
- agentdata/company/co_rues.py +72 -0
- agentdata/company/edgar_cik.py +84 -0
- agentdata/company/edgar_fts.py +101 -0
- agentdata/company/faces.py +746 -0
- agentdata/company/fdic_bankfind.py +107 -0
- agentdata/company/fi_ytj.py +89 -0
- agentdata/company/fr_recherche.py +124 -0
- agentdata/company/fts.py +121 -0
- agentdata/company/gleif.py +178 -0
- agentdata/company/kr_hometax.py +112 -0
- agentdata/company/no_brreg.py +124 -0
- agentdata/company/ror.py +105 -0
- agentdata/company/ted.py +117 -0
- agentdata/company/usaspending.py +124 -0
- agentdata/company/vies.py +66 -0
- agentdata/daterange.py +35 -0
- agentdata/economy/__init__.py +10 -0
- agentdata/economy/bis.py +132 -0
- agentdata/economy/cli.py +199 -0
- agentdata/economy/ecb.py +117 -0
- agentdata/economy/eurostat.py +163 -0
- agentdata/economy/faces.py +573 -0
- agentdata/economy/fred.py +92 -0
- agentdata/economy/ilo_stat.py +99 -0
- agentdata/economy/imf_sdmx.py +133 -0
- agentdata/economy/un_comtrade.py +199 -0
- agentdata/economy/unsd_sdg.py +154 -0
- agentdata/economy/wb.py +208 -0
- agentdata/economy/who_xmart.py +177 -0
- agentdata/entries.py +30 -0
- agentdata/errors.py +38 -0
- agentdata/esg/__init__.py +5 -0
- agentdata/esg/cli.py +180 -0
- agentdata/esg/climatetrace.py +260 -0
- agentdata/esg/eiti.py +187 -0
- agentdata/esg/epa_frs.py +138 -0
- agentdata/esg/euasl.py +75 -0
- agentdata/esg/eutl.py +66 -0
- agentdata/esg/faces.py +498 -0
- agentdata/esg/paris_mou.py +240 -0
- agentdata/esg/thetis_mrv.py +67 -0
- agentdata/healthcare/__init__.py +7 -0
- agentdata/healthcare/au_tga.py +142 -0
- agentdata/healthcare/ca_dpd.py +123 -0
- agentdata/healthcare/ca_mdall.py +155 -0
- agentdata/healthcare/cli.py +375 -0
- agentdata/healthcare/ctgov.py +101 -0
- agentdata/healthcare/dailymed.py +102 -0
- agentdata/healthcare/epmc.py +108 -0
- agentdata/healthcare/eudamed.py +124 -0
- agentdata/healthcare/faces.py +811 -0
- agentdata/healthcare/fda_crl.py +86 -0
- agentdata/healthcare/fda_devices.py +199 -0
- agentdata/healthcare/fda_drugs.py +156 -0
- agentdata/healthcare/fda_recalls.py +137 -0
- agentdata/healthcare/gb_mhra.py +137 -0
- agentdata/healthcare/gudid.py +84 -0
- agentdata/healthcare/mesh.py +89 -0
- agentdata/healthcare/rasff.py +164 -0
- agentdata/healthcare/rxnav.py +67 -0
- agentdata/healthcare/safety_gate.py +175 -0
- agentdata/healthcare/shortages.py +107 -0
- agentdata/legal/__init__.py +4 -0
- agentdata/legal/afdb.py +93 -0
- agentdata/legal/ca_sema.py +143 -0
- agentdata/legal/cli.py +312 -0
- agentdata/legal/co_sic.py +70 -0
- agentdata/legal/courtlistener.py +77 -0
- agentdata/legal/csl.py +139 -0
- agentdata/legal/dgcomp.py +125 -0
- agentdata/legal/ecfr.py +129 -0
- agentdata/legal/faces.py +700 -0
- agentdata/legal/federalregister.py +188 -0
- agentdata/legal/gb_cma.py +117 -0
- agentdata/legal/gb_tna_caselaw.py +245 -0
- agentdata/legal/idb.py +85 -0
- agentdata/legal/lda.py +147 -0
- agentdata/legal/sipri.py +178 -0
- agentdata/legal/ukec.py +148 -0
- agentdata/legal/wb.py +92 -0
- agentdata/procurement/__init__.py +5 -0
- agentdata/procurement/br_pncp.py +126 -0
- agentdata/procurement/cli.py +155 -0
- agentdata/procurement/co_secop.py +88 -0
- agentdata/procurement/faces.py +239 -0
- agentdata/procurement/ny_open_data.py +82 -0
- agentdata/procurement/ungm.py +137 -0
- agentdata/procurement/wb_proc.py +84 -0
- agentdata/research/__init__.py +4 -0
- agentdata/research/biorxiv.py +66 -0
- agentdata/research/cli.py +129 -0
- agentdata/research/crossref.py +121 -0
- agentdata/research/faces.py +349 -0
- agentdata/research/gb_gtr.py +58 -0
- agentdata/research/icite.py +52 -0
- agentdata/research/nih_reporter.py +143 -0
- agentdata/research/nsf.py +68 -0
- agentdata/research/openalex.py +145 -0
- agentdata/research/opencitations.py +50 -0
- agentdata/security/__init__.py +3 -0
- agentdata/security/cisa_kev.py +42 -0
- agentdata/security/cli.py +37 -0
- agentdata/security/faces.py +91 -0
- agentdata/security/osv.py +72 -0
- agentdata/transport.py +541 -0
- agentdata-0.3.26.dist-info/METADATA +266 -0
- agentdata-0.3.26.dist-info/RECORD +120 -0
- agentdata-0.3.26.dist-info/WHEEL +5 -0
- agentdata-0.3.26.dist-info/entry_points.txt +10 -0
- agentdata-0.3.26.dist-info/licenses/LICENSE +201 -0
- agentdata-0.3.26.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,573 @@
|
|
|
1
|
+
"""economy faces — flat face-method mixin (the eighth domain, stood up in 0.3.23).
|
|
2
|
+
|
|
3
|
+
Mixed into the root _Domain (MRO assembly, see client.py); discipline: no __init__, depends only
|
|
4
|
+
on root internals (_flat_env/_c/_request…), method names globally unique across domains (a
|
|
5
|
+
collision would shadow another domain's face).
|
|
6
|
+
|
|
7
|
+
The economy domain is the country-level denominator layer: macro indicators, reference rates,
|
|
8
|
+
official statistics and the trade mirror — the numbers the other five domains normalise against
|
|
9
|
+
(TAM denominators, amount conversion, income-band buyer screens). The subject is the country
|
|
10
|
+
(ISO code), never the company: these faces emit no company fact and occupy no rec_type.
|
|
11
|
+
"""
|
|
12
|
+
from ..errors import SdkError
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
from .. import economy # call via the domain-module path (historical shape, kept to minimize churn)
|
|
16
|
+
|
|
17
|
+
class EconomyFaces:
|
|
18
|
+
def _economy_only(self, tool: str):
|
|
19
|
+
if self.name != "economy":
|
|
20
|
+
raise SdkError("usage_error", detail=f"{tool} is an economy-domain capability (unavailable on the {self.name} domain)")
|
|
21
|
+
|
|
22
|
+
# ---------- World Bank WDI (global, keyless) ----------
|
|
23
|
+
|
|
24
|
+
def wb_wdi_values(self, *, country=None, indicator=None, date=None, per_page=100,
|
|
25
|
+
execution=None, no_cache=None):
|
|
26
|
+
"""economy wb_wdi_values: World Bank WDI indicator values for one or more countries
|
|
27
|
+
(/v2/country/{country}/indicator/{indicator}; country = ISO3, several codes joined by
|
|
28
|
+
';'; indicator = ONE WDI code — the upstream rejects several codes in one call
|
|
29
|
+
("120 Invalid value"); date = "YYYY" or "YYYY:YYYY"; a missing observation is null)."""
|
|
30
|
+
self._economy_only("wb_wdi_values")
|
|
31
|
+
r = economy.wb.wdi_values_raw(country, indicator, date=date, per_page=per_page,
|
|
32
|
+
cache=self._c._cache,
|
|
33
|
+
no_cache=self._c.no_cache if no_cache is None else bool(no_cache))
|
|
34
|
+
result = {k: x for k, x in r.items() if k != "_meta"}
|
|
35
|
+
top = (result["rows"] or [None])[0]
|
|
36
|
+
return self._flat_env(
|
|
37
|
+
tool="wb_wdi_values",
|
|
38
|
+
input={"country": country, "indicator": indicator, "date": date,
|
|
39
|
+
"per_page": r["params"]["per_page"]},
|
|
40
|
+
reason=(f"World Bank WDI: {r['params']['country']} × {r['params']['indicator']} — "
|
|
41
|
+
f"{result['count']} rows"
|
|
42
|
+
+ (f" (total {result['total']})" if result.get("total") is not None else "")
|
|
43
|
+
+ (f", last updated {result['last_updated']}" if result.get("last_updated") else "")
|
|
44
|
+
+ (f", top {top['country_iso3']} {top['year']} = {top['value']}" if top else ", zero rows")),
|
|
45
|
+
result=result,
|
|
46
|
+
evidence=[{"quote": f"{x['country_iso3']} {x['year']} = {x['value']} ({x['indicator_name'] or x['indicator_code']})",
|
|
47
|
+
"url": f"https://data.worldbank.org/indicator/{x['indicator_code']}?locations={x['country_iso3']}"}
|
|
48
|
+
for x in result["rows"][:5]],
|
|
49
|
+
provider_id="wb", metas=[r["_meta"]],
|
|
50
|
+
query={"source": "raw", "upstream": r["params"]},
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
def wb_wdi_indicators(self, *, source=2, page=1, per_page=500, search=None,
|
|
54
|
+
execution=None, no_cache=None):
|
|
55
|
+
"""economy wb_wdi_indicators: the World Bank indicator catalog (/v2/indicator;
|
|
56
|
+
source = the World Bank source id, 2 = WDI — without it the catalog is every source at
|
|
57
|
+
once (29,544 rows); search = a LOCAL keyword scan over the returned page, not an
|
|
58
|
+
upstream filter)."""
|
|
59
|
+
self._economy_only("wb_wdi_indicators")
|
|
60
|
+
r = economy.wb.wdi_indicators_raw(source=source, page=page, per_page=per_page,
|
|
61
|
+
search=search, cache=self._c._cache,
|
|
62
|
+
no_cache=self._c.no_cache if no_cache is None else bool(no_cache))
|
|
63
|
+
result = {k: x for k, x in r.items() if k != "_meta"}
|
|
64
|
+
top = (result["rows"] or [None])[0]
|
|
65
|
+
return self._flat_env(
|
|
66
|
+
tool="wb_wdi_indicators",
|
|
67
|
+
input={"source": r["params"]["source"], "page": r["params"]["page"],
|
|
68
|
+
"per_page": r["params"]["per_page"], "search": search},
|
|
69
|
+
reason=(f"World Bank indicator catalog (source {r['params']['source']}) — "
|
|
70
|
+
f"{result['total']:,} indicators in total, {result['count']} on this page"
|
|
71
|
+
+ (f", top {top['indicator_code']} = {top['name']}" if top else "")
|
|
72
|
+
+ (" — the keyword filter is a local scan over this page" if search else "")),
|
|
73
|
+
result=result,
|
|
74
|
+
evidence=[{"quote": f"{x['indicator_code']} = {x['name']} ({x['source_name']})",
|
|
75
|
+
"url": f"https://data.worldbank.org/indicator/{x['indicator_code']}"}
|
|
76
|
+
for x in result["rows"][:5]],
|
|
77
|
+
provider_id="wb", metas=[r["_meta"]],
|
|
78
|
+
query={"source": "raw", "upstream": r["params"]},
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
def wb_countries(self, *, page=1, per_page=100, execution=None, no_cache=None):
|
|
82
|
+
"""economy wb_countries: the World Bank country reference (/v2/country — ISO3 / ISO2 /
|
|
83
|
+
region / income level / lending type; the income band is the buyer-screen dimension
|
|
84
|
+
and the 296 entries include the aggregate regions)."""
|
|
85
|
+
self._economy_only("wb_countries")
|
|
86
|
+
r = economy.wb.countries_raw(page=page, per_page=per_page, cache=self._c._cache,
|
|
87
|
+
no_cache=self._c.no_cache if no_cache is None else bool(no_cache))
|
|
88
|
+
result = {k: x for k, x in r.items() if k != "_meta"}
|
|
89
|
+
top = (result["rows"] or [None])[0]
|
|
90
|
+
return self._flat_env(
|
|
91
|
+
tool="wb_countries",
|
|
92
|
+
input={"page": r["params"]["page"], "per_page": r["params"]["per_page"]},
|
|
93
|
+
reason=(f"World Bank country reference — {result['total']} entries in total, "
|
|
94
|
+
f"{result['count']} on this page"
|
|
95
|
+
+ (f", top {top['iso3']} {top['name']} ({top['income_level']})" if top else "")),
|
|
96
|
+
result=result,
|
|
97
|
+
evidence=[{"quote": f"{x['iso3']} {x['name']} ({x['income_level']}, {x['region_name']})",
|
|
98
|
+
"url": f"https://data.worldbank.org/country/{x['iso3']}"}
|
|
99
|
+
for x in result["rows"][:5]],
|
|
100
|
+
provider_id="wb", metas=[r["_meta"]],
|
|
101
|
+
query={"source": "raw", "upstream": r["params"]},
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
def wb_ids_indicators(self, *, page=1, per_page=600, search=None,
|
|
105
|
+
execution=None, no_cache=None):
|
|
106
|
+
"""economy wb_ids_indicators: the World Bank International Debt Statistics catalog
|
|
107
|
+
(/v2/indicator with source pinned to 6 = IDS — 577 indicators in seven debt families:
|
|
108
|
+
DOD stocks / INT interest / AMT disbursements / DIS / NFL net flows / NTR / TDS service
|
|
109
|
+
plus the balance-of-payments mirrors; the per-page default 600 holds the whole catalog
|
|
110
|
+
in one page). The VALUES of the DT.* codes are served by wb_wdi_values WITHOUT a source
|
|
111
|
+
parameter — the value endpoint breaks with source=6 ("175 indicator was not found";
|
|
112
|
+
the API silently falls back to sourceid 2)."""
|
|
113
|
+
self._economy_only("wb_ids_indicators")
|
|
114
|
+
r = economy.wb.wdi_indicators_raw(source=6, page=page, per_page=per_page,
|
|
115
|
+
search=search, cache=self._c._cache,
|
|
116
|
+
no_cache=self._c.no_cache if no_cache is None else bool(no_cache))
|
|
117
|
+
result = {k: x for k, x in r.items() if k != "_meta"}
|
|
118
|
+
top = (result["rows"] or [None])[0]
|
|
119
|
+
return self._flat_env(
|
|
120
|
+
tool="wb_ids_indicators",
|
|
121
|
+
input={"page": r["params"]["page"], "per_page": r["params"]["per_page"],
|
|
122
|
+
"search": search},
|
|
123
|
+
reason=(f"World Bank International Debt Statistics catalog (source 6) — "
|
|
124
|
+
f"{result['total']} indicators in total, {result['count']} on this page"
|
|
125
|
+
+ (f", top {top['indicator_code']} = {top['name']}" if top else "")
|
|
126
|
+
+ (" — the keyword filter is a local scan over this page" if search else "")
|
|
127
|
+
+ " — the DT.* values are served by wb_wdi_values WITHOUT --source"),
|
|
128
|
+
result=result,
|
|
129
|
+
evidence=[{"quote": f"{x['indicator_code']} = {x['name']}",
|
|
130
|
+
"url": f"https://data.worldbank.org/indicator/{x['indicator_code']}"}
|
|
131
|
+
for x in result["rows"][:5]],
|
|
132
|
+
provider_id="wb", metas=[r["_meta"]],
|
|
133
|
+
query={"source": "raw", "upstream": r["params"]},
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# ---------- Eurostat (EU) ----------
|
|
137
|
+
|
|
138
|
+
def eurostat_table(self, *, table=None, geo=None, time=None, dims=None, since=None,
|
|
139
|
+
until=None, lang="EN", execution=None, no_cache=None):
|
|
140
|
+
"""economy eurostat_table: one Eurostat table through the filters you give
|
|
141
|
+
(/data/{table}; geo / time / dims = the upstream dimension filters — several values
|
|
142
|
+
are REPEATED parameters; dims = the table-specific dimension ids as k=v pairs;
|
|
143
|
+
since / until = the upstream sinceTimePeriod / untilTimePeriod window; the JSON-stat
|
|
144
|
+
grid is decoded row-major into flat rows)."""
|
|
145
|
+
self._economy_only("eurostat_table")
|
|
146
|
+
r = economy.eurostat.table_raw(table, geo=geo, time=time, dims=dims, since=since,
|
|
147
|
+
until=until, lang=lang, cache=self._c._cache,
|
|
148
|
+
no_cache=self._c.no_cache if no_cache is None else bool(no_cache))
|
|
149
|
+
result = {k: x for k, x in r.items() if k != "_meta"}
|
|
150
|
+
top = (result["rows"] or [None])[0]
|
|
151
|
+
# The dimension dictionary carries every code -> label pair (tens of KB for a wide
|
|
152
|
+
# table), so the reason line names the dimension ids and their code counts, and the
|
|
153
|
+
# top cell spells out the dimension codes it sits at.
|
|
154
|
+
dims_shown = ", ".join(
|
|
155
|
+
f"{k} ({len(d.get('codes') or [])} code{'s' if len(d.get('codes') or []) != 1 else ''})"
|
|
156
|
+
for k, d in (result.get("dimensions") or {}).items() if k not in ("geo", "time"))
|
|
157
|
+
top_dims = ""
|
|
158
|
+
if top:
|
|
159
|
+
extra = " ".join(f"{k}={v}" for k, v in top.items()
|
|
160
|
+
if k not in ("geo", "time", "value", "status"))
|
|
161
|
+
top_dims = f" {extra}" if extra else ""
|
|
162
|
+
return self._flat_env(
|
|
163
|
+
tool="eurostat_table",
|
|
164
|
+
input={"table": table, "geo": geo, "time": time, "dims": dims,
|
|
165
|
+
"since": since, "until": until, "lang": lang},
|
|
166
|
+
reason=(f"Eurostat {result.get('label') or table} — {result['count']} cells"
|
|
167
|
+
+ (f" (table holds {result['total']:,})" if result.get("total") else "")
|
|
168
|
+
+ (f", updated {result['updated']}" if result.get("updated") else "")
|
|
169
|
+
+ (f", dims {dims_shown}" if dims_shown else "")
|
|
170
|
+
+ (f", top {top.get('geo', '—')} {top.get('time', '—')}{top_dims} = {top.get('value')}"
|
|
171
|
+
if top else ", zero cells")
|
|
172
|
+
+ (" — a geo outside the table yields zero cells and NO upstream error" if not result["count"] else "")),
|
|
173
|
+
result=result,
|
|
174
|
+
evidence=[{"quote": " ".join(f"{k}={v}" for k, v in x.items() if k != "value") + f" = {x.get('value')}",
|
|
175
|
+
"url": f"https://ec.europa.eu/eurostat/databrowser/view/{table}/default/table?lang=en"}
|
|
176
|
+
for x in result["rows"][:5]],
|
|
177
|
+
provider_id="eurostat", metas=[r["_meta"]],
|
|
178
|
+
query={"source": "raw", "upstream": r["params"]},
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
# ---------- ECB reference rates (EU) ----------
|
|
182
|
+
|
|
183
|
+
def ecb_fx_rates(self, *, currency=None, start=None, end=None, execution=None, no_cache=None):
|
|
184
|
+
"""economy ecb_fx_rates: the ECB euro reference rate for one currency
|
|
185
|
+
(/EXR/D.{CCY}.EUR.SP00.A, csvdata; the value is units of {currency} per 1 EUR; start /
|
|
186
|
+
end = the upstream startPeriod / endPeriod window; the historical eurofxref CSV is a
|
|
187
|
+
stale 2010 snapshot and is NOT used — this live SDMX endpoint is)."""
|
|
188
|
+
self._economy_only("ecb_fx_rates")
|
|
189
|
+
r = economy.ecb.fx_rates_raw(currency, start=start, end=end, cache=self._c._cache,
|
|
190
|
+
no_cache=self._c.no_cache if no_cache is None else bool(no_cache))
|
|
191
|
+
result = {k: x for k, x in r.items() if k != "_meta"}
|
|
192
|
+
# the ECB rows are sorted ASCENDING by date — the latest observation is the LAST row
|
|
193
|
+
top = (result["rows"] or [None])[-1]
|
|
194
|
+
return self._flat_env(
|
|
195
|
+
tool="ecb_fx_rates",
|
|
196
|
+
input={"currency": r["params"]["currency"], "start": start, "end": end},
|
|
197
|
+
reason=(f"ECB reference rate {r['params']['currency']}/EUR — {result['count']} daily observations"
|
|
198
|
+
+ (f" {r['params']['start']}→{r['params']['end']}" if r["params"].get("start") or r["params"].get("end") else "")
|
|
199
|
+
+ (f", latest {top['date']} = {top['rate']}" if top else ", zero observations")),
|
|
200
|
+
result=result,
|
|
201
|
+
evidence=[{"quote": f"{x['date']}: 1 EUR = {x['rate']} {x['currency']}",
|
|
202
|
+
"url": "https://www.ecb.europa.eu/stats/policy_and_exchange_rates/euro_reference_exchange_rates/html/index.en.html"}
|
|
203
|
+
for x in result["rows"][:5]],
|
|
204
|
+
provider_id="ecb", metas=[r["_meta"]],
|
|
205
|
+
query={"source": "raw", "upstream": r["params"]},
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
# ---------- FRED (US) ----------
|
|
209
|
+
|
|
210
|
+
def fred_series(self, *, series=None, start=None, end=None, execution=None, no_cache=None):
|
|
211
|
+
"""economy fred_series: one FRED series over a date window (/fredgraph.csv?id=;
|
|
212
|
+
ONE series id per call — several ids answer a ZIP archive, which the JSON transport
|
|
213
|
+
cannot carry; start / end = the upstream cosd / coed window; an unknown series id
|
|
214
|
+
answers an HTML error page and is reported as not-found)."""
|
|
215
|
+
self._economy_only("fred_series")
|
|
216
|
+
r = economy.fred.series_raw(series, start=start, end=end, cache=self._c._cache,
|
|
217
|
+
no_cache=self._c.no_cache if no_cache is None else bool(no_cache))
|
|
218
|
+
result = {k: x for k, x in r.items() if k != "_meta"}
|
|
219
|
+
# the FRED rows are sorted ASCENDING by date — the latest observation is the LAST row
|
|
220
|
+
top = (result["rows"] or [None])[-1]
|
|
221
|
+
return self._flat_env(
|
|
222
|
+
tool="fred_series",
|
|
223
|
+
input={"series": r["params"]["series"], "start": start, "end": end},
|
|
224
|
+
reason=(f"FRED {r['params']['series']} — {result['count']} observations"
|
|
225
|
+
+ (f", first {result['rows'][0]['date']}, latest {top['date']} = {top['value']}"
|
|
226
|
+
if top else ", zero observations")),
|
|
227
|
+
result=result,
|
|
228
|
+
evidence=[{"quote": f"{x['date']}: {x['value']} ({x['series']})",
|
|
229
|
+
"url": f"https://fred.stlouisfed.org/series/{x['series']}"}
|
|
230
|
+
for x in result["rows"][:5]],
|
|
231
|
+
provider_id="fred", metas=[r["_meta"]],
|
|
232
|
+
query={"source": "raw", "upstream": r["params"]},
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
# ---------- WHO xMart (global) ----------
|
|
236
|
+
|
|
237
|
+
def who_xmart_indicators(self, *, filter=None, top=500, skip=0, search=None,
|
|
238
|
+
execution=None, no_cache=None):
|
|
239
|
+
"""economy who_xmart_indicators: the WHO xMart indicator directory
|
|
240
|
+
(IND_DIRECTORY_WIDE — 1,561 indicators, each row carrying IND_URI_API, the per-indicator
|
|
241
|
+
API diameter, and IND_URI_WEB; filter = the raw upstream OData $filter; search = a LOCAL
|
|
242
|
+
keyword scan over the returned page)."""
|
|
243
|
+
self._economy_only("who_xmart_indicators")
|
|
244
|
+
r = economy.who_xmart.indicator_directory_raw(filter=filter, top=top, skip=skip,
|
|
245
|
+
search=search, cache=self._c._cache,
|
|
246
|
+
no_cache=self._c.no_cache if no_cache is None else bool(no_cache))
|
|
247
|
+
result = {k: x for k, x in r.items() if k != "_meta"}
|
|
248
|
+
top = (result["rows"] or [None])[0]
|
|
249
|
+
filtered = bool(r["params"].get("filter"))
|
|
250
|
+
head = (f"WHO xMart indicator directory — {result['total']} indicators "
|
|
251
|
+
+ ("match the upstream $filter" if filtered else "in the catalog"))
|
|
252
|
+
return self._flat_env(
|
|
253
|
+
tool="who_xmart_indicators",
|
|
254
|
+
input={"filter": r["params"]["filter"], "top": r["params"]["top"],
|
|
255
|
+
"skip": r["params"]["skip"], "search": search},
|
|
256
|
+
reason=(f"{head}, {result['count']} on this page"
|
|
257
|
+
+ (f", top {top['IND_NAME']} — {top.get('IND_NAME_FULL') or top.get('IND_UNIT')}" if top else "")
|
|
258
|
+
+ (" — the keyword filter is a local scan over this page" if search else "")),
|
|
259
|
+
result=result,
|
|
260
|
+
evidence=[{"quote": f"{x['IND_NAME']} — {x.get('IND_NAME_FULL') or x.get('IND_UNIT') or ''}".strip(),
|
|
261
|
+
"url": x.get("IND_URI_WEB") or f"https://xmart-api-public.who.int/DATA_/IND_DIRECTORY_WIDE"}
|
|
262
|
+
for x in result["rows"][:5]],
|
|
263
|
+
provider_id="who_xmart", metas=[r["_meta"]],
|
|
264
|
+
query={"source": "raw", "upstream": r["params"]},
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
def who_xmart_values(self, *, indicator=None, geo=None, year=None, entity_set=None,
|
|
268
|
+
top=500, skip=0, execution=None, no_cache=None):
|
|
269
|
+
"""economy who_xmart_values: the observations of one WHO indicator
|
|
270
|
+
(DATA_/{entity set}; the entity set comes from the directory row's IND_URI_API — when
|
|
271
|
+
several entity sets carry the same indicator name the candidate list is NOT auto-picked;
|
|
272
|
+
geo = an ISO-3 country (translated through REF_GEO_WIDE into the M49 code the value
|
|
273
|
+
tables use) or a raw M49 code; year = the DIM_TIME value (a string, so it is quoted)."""
|
|
274
|
+
self._economy_only("who_xmart_values")
|
|
275
|
+
code = str(indicator or "").strip()
|
|
276
|
+
if not code:
|
|
277
|
+
raise SdkError("usage_error", provider="who_xmart",
|
|
278
|
+
detail="who_xmart_values requires --indicator (the indicator code, "
|
|
279
|
+
"e.g. TB_ppm_enrol_tx_prct — list them with who_xmart_indicators)")
|
|
280
|
+
metas = []
|
|
281
|
+
es = str(entity_set or "").strip().strip("/")
|
|
282
|
+
if not es:
|
|
283
|
+
rows, dir_meta = economy.who_xmart.entity_sets_for_indicator(
|
|
284
|
+
code, cache=self._c._cache,
|
|
285
|
+
no_cache=self._c.no_cache if no_cache is None else bool(no_cache))
|
|
286
|
+
metas.append(dir_meta)
|
|
287
|
+
if not rows:
|
|
288
|
+
raise SdkError("upstream_not_found", provider="who_xmart",
|
|
289
|
+
detail=f"No WHO xMart indicator named '{code}' — list the catalog "
|
|
290
|
+
"with who_xmart_indicators")
|
|
291
|
+
sets = []
|
|
292
|
+
for row in rows:
|
|
293
|
+
uri = row.get("IND_URI_API") or ""
|
|
294
|
+
name = uri.rstrip("/").split("/")[-1]
|
|
295
|
+
if name and name not in sets:
|
|
296
|
+
sets.append(name)
|
|
297
|
+
if len(sets) > 1:
|
|
298
|
+
raise SdkError("usage_error", provider="who_xmart",
|
|
299
|
+
detail=f"The indicator '{code}' is published in {len(sets)} entity "
|
|
300
|
+
f"sets ({', '.join(sets)}) — pick one explicitly with "
|
|
301
|
+
"--entity-set (the candidate list is never auto-picked)")
|
|
302
|
+
es = sets[0]
|
|
303
|
+
geo_value = None
|
|
304
|
+
geo_meta = None
|
|
305
|
+
if geo:
|
|
306
|
+
g = str(geo).strip()
|
|
307
|
+
if g.isdigit():
|
|
308
|
+
geo_value = g
|
|
309
|
+
else:
|
|
310
|
+
m49, geo_rows, geo_meta = economy.who_xmart.m49_for_iso3(
|
|
311
|
+
g, cache=self._c._cache,
|
|
312
|
+
no_cache=self._c.no_cache if no_cache is None else bool(no_cache))
|
|
313
|
+
if not m49:
|
|
314
|
+
raise SdkError("upstream_not_found", provider="who_xmart",
|
|
315
|
+
detail=f"No WHO geography row for '{g}' (REF_GEO_WIDE has no "
|
|
316
|
+
"such ISO-3 code)")
|
|
317
|
+
geo_value = m49
|
|
318
|
+
odata = [f"IND_CODE eq '{code}'"]
|
|
319
|
+
if year:
|
|
320
|
+
odata.append(f"DIM_TIME eq '{str(year).strip()}'")
|
|
321
|
+
if geo_value:
|
|
322
|
+
odata.append(f"DIM_GEO_CODE_M49 eq '{geo_value}'")
|
|
323
|
+
r = economy.who_xmart.indicator_values_raw(es, filter=" and ".join(odata), top=top,
|
|
324
|
+
skip=skip, cache=self._c._cache,
|
|
325
|
+
no_cache=self._c.no_cache if no_cache is None else bool(no_cache))
|
|
326
|
+
metas.append(r["_meta"])
|
|
327
|
+
if geo_meta:
|
|
328
|
+
metas.append(geo_meta)
|
|
329
|
+
result = {k: x for k, x in r.items() if k != "_meta"}
|
|
330
|
+
top_row = (result["rows"] or [None])[0]
|
|
331
|
+
return self._flat_env(
|
|
332
|
+
tool="who_xmart_values",
|
|
333
|
+
input={"indicator": code, "geo": geo, "year": year, "entity_set": es,
|
|
334
|
+
"top": r["params"]["top"], "skip": r["params"]["skip"]},
|
|
335
|
+
reason=(f"WHO xMart {code} (entity set {es}) — {result['count']} observations"
|
|
336
|
+
+ (f", geo {geo_value}" if geo_value else "")
|
|
337
|
+
+ (f", {year}" if year else "")
|
|
338
|
+
+ (f", top {top_row['DIM_TIME']} {top_row['DIM_GEO_CODE_M49']} = {top_row['VALUE_NUMERIC']}"
|
|
339
|
+
if top_row else ", zero observations")),
|
|
340
|
+
result=result,
|
|
341
|
+
evidence=[{"quote": f"{x.get('DIM_TIME')} {x.get('DIM_GEO_CODE_M49')} {x.get('DIM_MEMBER_1_CODE') or ''} = {x.get('VALUE_NUMERIC')}".strip(),
|
|
342
|
+
"url": f"https://xmart-api-public.who.int/DATA_/{es}"}
|
|
343
|
+
for x in result["rows"][:5]],
|
|
344
|
+
provider_id="who_xmart", metas=metas,
|
|
345
|
+
query={"source": "raw", "upstream": r["params"]},
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
# ---------- UN Comtrade (global) ----------
|
|
349
|
+
|
|
350
|
+
def un_comtrade_trade(self, *, reporter=None, period=None, partner=None, cmd=None,
|
|
351
|
+
flow=None, type_code="C", freq_code="A", cl_code="HS",
|
|
352
|
+
max_records=500, partner2=None, customs=None, mot=None,
|
|
353
|
+
execution=None, no_cache=None):
|
|
354
|
+
"""economy un_comtrade_trade: the UN Comtrade trade mirror (public preview API;
|
|
355
|
+
reporter / partner = an ISO-3 code or the numeric area code — the ISO3 is translated
|
|
356
|
+
through the area reference, whose historical rows and duplicate ISO3 entries make a
|
|
357
|
+
naive map wrong (USA is 842, not 840 or 841); period = 2023 / 202301; cmd = the
|
|
358
|
+
commodity code (a comma list, TOTAL = all goods); flow = X / M / "X,M")."""
|
|
359
|
+
self._economy_only("un_comtrade_trade")
|
|
360
|
+
r = economy.un_comtrade.trade_raw(reporter, period=period, partner=partner, cmd=cmd,
|
|
361
|
+
flow=flow, type_code=type_code, freq_code=freq_code,
|
|
362
|
+
cl_code=cl_code, max_records=max_records,
|
|
363
|
+
partner2=partner2, customs=customs, mot=mot,
|
|
364
|
+
cache=self._c._cache,
|
|
365
|
+
no_cache=self._c.no_cache if no_cache is None else bool(no_cache))
|
|
366
|
+
result = {k: x for k, x in r.items() if k != "_meta"}
|
|
367
|
+
top = (result["rows"] or [None])[0]
|
|
368
|
+
return self._flat_env(
|
|
369
|
+
tool="un_comtrade_trade",
|
|
370
|
+
input={"reporter": reporter, "period": period, "partner": partner, "cmd": cmd,
|
|
371
|
+
"flow": flow, "type_code": type_code, "freq_code": freq_code,
|
|
372
|
+
"cl_code": cl_code, "max_records": max_records},
|
|
373
|
+
reason=(f"UN Comtrade {reporter} × {partner or 'World'} — {period}, "
|
|
374
|
+
f"cmd {cmd or 'TOTAL'}, flow {flow or 'all'}: {result['count']} rows"
|
|
375
|
+
+ (f", top {top['flow_name']} {top['cmd_code']} = {top['primary_value']} USD"
|
|
376
|
+
if top else ", zero rows")),
|
|
377
|
+
result=result,
|
|
378
|
+
evidence=[{"quote": f"{x['period']} {x['reporter_iso3']} → {x['partner_iso3']} "
|
|
379
|
+
f"{x['flow_name']} {x['cmd_code']} = {x['primary_value']} USD",
|
|
380
|
+
"url": "https://comtradeplus.un.org/"}
|
|
381
|
+
for x in result["rows"][:5]],
|
|
382
|
+
provider_id="un_comtrade", metas=[r["_meta"]],
|
|
383
|
+
query={"source": "raw", "upstream": r["params"]},
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
# ---------- ILOSTAT (global) ----------
|
|
387
|
+
|
|
388
|
+
def ilo_stat_indicator(self, *, indicator=None, ref_area=None, time_from=None,
|
|
389
|
+
time_until=None, sex=None, classif=None, source=None,
|
|
390
|
+
execution=None, no_cache=None):
|
|
391
|
+
"""economy ilo_stat_indicator: one ILOSTAT indicator for one country
|
|
392
|
+
(/data/indicator/; indicator = the ILOSTAT code; ref_area = the ISO-3 country — the
|
|
393
|
+
world-wide answer is orders of magnitude bigger, so the country is required;
|
|
394
|
+
time_from / time_until = the year bounds; sex / classif / source = the upstream
|
|
395
|
+
dimension filters)."""
|
|
396
|
+
self._economy_only("ilo_stat_indicator")
|
|
397
|
+
r = economy.ilo_stat.indicator_raw(indicator, ref_area=ref_area, time_from=time_from,
|
|
398
|
+
time_until=time_until, sex=sex, classif=classif,
|
|
399
|
+
source=source, cache=self._c._cache,
|
|
400
|
+
no_cache=self._c.no_cache if no_cache is None else bool(no_cache))
|
|
401
|
+
result = {k: x for k, x in r.items() if k != "_meta"}
|
|
402
|
+
top = (result["rows"] or [None])[0] # the rows are sorted newest year first
|
|
403
|
+
top_dims = ""
|
|
404
|
+
if top:
|
|
405
|
+
bits = [str(top.get("sex") or ""), str(top.get("classif1") or "")]
|
|
406
|
+
top_dims = " " + " ".join(b for b in bits if b)
|
|
407
|
+
win = ""
|
|
408
|
+
if r["params"].get("time_from") and r["params"].get("time_until"):
|
|
409
|
+
win = f" {r['params']['time_from']}→{r['params']['time_until']}"
|
|
410
|
+
elif r["params"].get("time_from"):
|
|
411
|
+
win = f" from {r['params']['time_from']}"
|
|
412
|
+
elif r["params"].get("time_until"):
|
|
413
|
+
win = f" until {r['params']['time_until']}"
|
|
414
|
+
return self._flat_env(
|
|
415
|
+
tool="ilo_stat_indicator",
|
|
416
|
+
input={"indicator": r["params"]["indicator"], "ref_area": r["params"]["ref_area"],
|
|
417
|
+
"time_from": time_from, "time_until": time_until, "sex": sex,
|
|
418
|
+
"classif": classif, "source": source},
|
|
419
|
+
reason=(f"ILOSTAT {r['params']['indicator']} × {r['params']['ref_area']} — "
|
|
420
|
+
f"{result['count']} observations{win}"
|
|
421
|
+
+ (f", latest {top['time']}{top_dims} = {top['obs_value']}" if top else ", zero observations")),
|
|
422
|
+
result=result,
|
|
423
|
+
evidence=[{"quote": f"{x['time']} {x.get('sex') or ''} {x.get('classif1') or ''} = {x['obs_value']}".strip(),
|
|
424
|
+
"url": f"https://rshiny.ilo.org/dataexplorer50/?lang=en&id={r['params']['indicator']}"}
|
|
425
|
+
for x in result["rows"][:5]],
|
|
426
|
+
provider_id="ilo_stat", metas=[r["_meta"]],
|
|
427
|
+
query={"source": "raw", "upstream": r["params"]},
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
# ---------- IMF SDMX 2.1 (global) ----------
|
|
431
|
+
|
|
432
|
+
def imf_sdmx_series(self, *, flow=None, country=None, indicator=None, frequency=None,
|
|
433
|
+
start=None, end=None, execution=None, no_cache=None):
|
|
434
|
+
"""economy imf_sdmx_series: one IMF dataflow series (/external/sdmx/2.1/data/{flow}/
|
|
435
|
+
{COUNTRY.INDICATOR[.FREQUENCY]}, the SDMX 2.1 generic-CSV channel; flow = WEO / GDD /
|
|
436
|
+
CPI …; country AND indicator are both required — the country-only key is the
|
|
437
|
+
multi-megabyte wall; the key shape fits the WEO/GDD/CPI family, NOT UNFCCC whose
|
|
438
|
+
dimensions insert INDUSTRY/GAS_TYPE before the indicator; start / end = the upstream
|
|
439
|
+
startPeriod / endPeriod bounds; OBS_VALUE carries the true value — SCALE is only a
|
|
440
|
+
display hint)."""
|
|
441
|
+
self._economy_only("imf_sdmx_series")
|
|
442
|
+
r = economy.imf_sdmx.sdmx_series_raw(flow, country, indicator, frequency=frequency,
|
|
443
|
+
start=start, end=end, cache=self._c._cache,
|
|
444
|
+
no_cache=self._c.no_cache if no_cache is None else bool(no_cache))
|
|
445
|
+
result = {k: x for k, x in r.items() if k != "_meta"}
|
|
446
|
+
# the rows are sorted ascending by period — the latest observation is the LAST row
|
|
447
|
+
top = (result["rows"] or [None])[-1]
|
|
448
|
+
win = (f" {r['params']['start']}→{r['params']['end']}"
|
|
449
|
+
if r["params"].get("start") and r["params"].get("end")
|
|
450
|
+
else (f" from {r['params']['start']}" if r["params"].get("start")
|
|
451
|
+
else (f" until {r['params']['end']}" if r["params"].get("end") else "")))
|
|
452
|
+
return self._flat_env(
|
|
453
|
+
tool="imf_sdmx_series",
|
|
454
|
+
input={"flow": r["params"]["flow"], "country": r["params"]["country"],
|
|
455
|
+
"indicator": r["params"]["indicator"], "frequency": r["params"]["frequency"],
|
|
456
|
+
"start": start, "end": end},
|
|
457
|
+
reason=(f"IMF SDMX {r['params']['flow']} {result['key']} — {result['count']} observations{win}"
|
|
458
|
+
+ (f", first {result['rows'][0]['period']}, latest {top['period']} = {top['value']}"
|
|
459
|
+
if top else ", zero observations")
|
|
460
|
+
+ (f" ({top['series_name']})" if top and top.get("series_name") else "")),
|
|
461
|
+
result=result,
|
|
462
|
+
evidence=[{"quote": f"{x['period']} {x['value']}"
|
|
463
|
+
+ (f" ({', '.join(f'{k}={v}' for k, v in x['dims'].items())})" if x["dims"] else ""),
|
|
464
|
+
"url": f"https://api.imf.org/external/sdmx/2.1/data/{r['params']['flow']}/{result['key']}"}
|
|
465
|
+
for x in result["rows"][:5]],
|
|
466
|
+
provider_id="imf_sdmx", metas=[r["_meta"]],
|
|
467
|
+
query={"source": "raw", "upstream": r["params"]},
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
# ---------- BIS SDMX (global) ----------
|
|
471
|
+
|
|
472
|
+
def bis_sdmx_series(self, *, flow=None, key=None, start=None, end=None,
|
|
473
|
+
execution=None, no_cache=None):
|
|
474
|
+
"""economy bis_sdmx_series: one BIS dataflow series (/api/v1/data/{flow}/{key}/all, the
|
|
475
|
+
SDMX 2.1 generic-CSV channel; key = the raw dot-separated series key with at least two
|
|
476
|
+
segments — the segment order is flow-specific: WS_CBPOL FREQ.REF_AREA (M.US), WS_DSR
|
|
477
|
+
FREQ.BORROWERS_CTY.DSR_BORROWERS (Q.US.N), WS_XRU FREQ.REF_AREA.CURRENCY.COLLECTION
|
|
478
|
+
(M.CN.CNY.E); a one-segment key is every area at once — the multi-megabyte wall; a
|
|
479
|
+
wrong key answers 404 with an SDMX error, it is not a block; start / end = the
|
|
480
|
+
upstream startPeriod / endPeriod bounds)."""
|
|
481
|
+
self._economy_only("bis_sdmx_series")
|
|
482
|
+
r = economy.bis.sdmx_series_raw(flow, key, start=start, end=end, cache=self._c._cache,
|
|
483
|
+
no_cache=self._c.no_cache if no_cache is None else bool(no_cache))
|
|
484
|
+
result = {k: x for k, x in r.items() if k != "_meta"}
|
|
485
|
+
# the rows are sorted ascending by period — the latest observation is the LAST row
|
|
486
|
+
top = (result["rows"] or [None])[-1]
|
|
487
|
+
win = (f" {r['params']['start']}→{r['params']['end']}"
|
|
488
|
+
if r["params"].get("start") and r["params"].get("end")
|
|
489
|
+
else (f" from {r['params']['start']}" if r["params"].get("start")
|
|
490
|
+
else (f" until {r['params']['end']}" if r["params"].get("end") else "")))
|
|
491
|
+
return self._flat_env(
|
|
492
|
+
tool="bis_sdmx_series",
|
|
493
|
+
input={"flow": r["params"]["flow"], "key": r["params"]["key"],
|
|
494
|
+
"start": start, "end": end},
|
|
495
|
+
reason=(f"BIS {r['params']['flow']} {result['key']} — {result['count']} observations{win}"
|
|
496
|
+
+ (f", first {result['rows'][0]['period']}, latest {top['period']} = {top['value']}"
|
|
497
|
+
if top else ", zero observations")
|
|
498
|
+
+ (f" ({top['title']})" if top and top.get("title") else "")),
|
|
499
|
+
result=result,
|
|
500
|
+
evidence=[{"quote": f"{x['period']} {x['value']}"
|
|
501
|
+
+ (f" ({', '.join(f'{k}={v}' for k, v in x['dims'].items())})" if x["dims"] else ""),
|
|
502
|
+
"url": f"https://stats.bis.org/api/v1/data/{r['params']['flow']}/{result['key']}/all"}
|
|
503
|
+
for x in result["rows"][:5]],
|
|
504
|
+
provider_id="bis", metas=[r["_meta"]],
|
|
505
|
+
query={"source": "raw", "upstream": r["params"]},
|
|
506
|
+
)
|
|
507
|
+
|
|
508
|
+
# ---------- UNSD SDG (global) ----------
|
|
509
|
+
|
|
510
|
+
def unsd_sdg_indicators(self, *, page=1, per_page=50, search=None,
|
|
511
|
+
execution=None, no_cache=None):
|
|
512
|
+
"""economy unsd_sdg_indicators: the UN SDG indicator catalog (/Indicator/List — 251
|
|
513
|
+
indicators over the 17 goals, each row carrying its series codes (713 in total) nested
|
|
514
|
+
inside; the upstream answers the whole catalog in one payload — page / per_page slice
|
|
515
|
+
it locally and search is a LOCAL keyword scan over the whole catalog, never an
|
|
516
|
+
upstream filter)."""
|
|
517
|
+
self._economy_only("unsd_sdg_indicators")
|
|
518
|
+
r = economy.unsd_sdg.indicators_raw(page=page, per_page=per_page, search=search,
|
|
519
|
+
cache=self._c._cache,
|
|
520
|
+
no_cache=self._c.no_cache if no_cache is None else bool(no_cache))
|
|
521
|
+
result = {k: x for k, x in r.items() if k != "_meta"}
|
|
522
|
+
top = (result["rows"] or [None])[0]
|
|
523
|
+
return self._flat_env(
|
|
524
|
+
tool="unsd_sdg_indicators",
|
|
525
|
+
input={"page": r["params"]["page"], "per_page": r["params"]["per_page"],
|
|
526
|
+
"search": search},
|
|
527
|
+
reason=(f"UNSD SDG indicator catalog — {result['total']} indicators "
|
|
528
|
+
+ ("match the keyword" if search else "over the 17 goals")
|
|
529
|
+
+ f", {result['count']} on this page"
|
|
530
|
+
+ (f", top {top['code']} (goal {top['goal']}, {top['series_count']} series) = "
|
|
531
|
+
f"{(top['description'] or '')[:80]}" if top else "")),
|
|
532
|
+
result=result,
|
|
533
|
+
evidence=[{"quote": f"{x['code']} (goal {x['goal']}) — {(x['description'] or '')[:80]}"
|
|
534
|
+
+ f" [{', '.join(x['series_codes'][:3])}{'…' if len(x['series_codes']) > 3 else ''}]",
|
|
535
|
+
"url": f"https://unstats.un.org/SDGAPI{x['uri']}" if x.get("uri")
|
|
536
|
+
else "https://unstats.un.org/SDGAPI/v1/sdg/Indicator/List"}
|
|
537
|
+
for x in result["rows"][:5]],
|
|
538
|
+
provider_id="unsd_sdg", metas=[r["_meta"]],
|
|
539
|
+
query={"source": "raw", "upstream": r["params"]},
|
|
540
|
+
)
|
|
541
|
+
|
|
542
|
+
def unsd_sdg_series(self, *, series=None, geo=None, time=None, page=1, page_size=100,
|
|
543
|
+
execution=None, no_cache=None):
|
|
544
|
+
"""economy unsd_sdg_series: the observations of ONE SDG series (/Series/Data?
|
|
545
|
+
seriesCode=… — series granularity only: the whole-indicator endpoint is a 3.2M-row
|
|
546
|
+
wall that is never formed; geo = a LOCAL filter over the fetched page matching the
|
|
547
|
+
geoAreaCode or geoAreaName — the upstream `area` parameter is DECORATIVE,
|
|
548
|
+
byte-identical with and without it; time = the upstream timePeriod filter, the one
|
|
549
|
+
upstream filter that works on this endpoint)."""
|
|
550
|
+
self._economy_only("unsd_sdg_series")
|
|
551
|
+
r = economy.unsd_sdg.series_raw(series, geo=geo, time=time, page=page,
|
|
552
|
+
page_size=page_size, cache=self._c._cache,
|
|
553
|
+
no_cache=self._c.no_cache if no_cache is None else bool(no_cache))
|
|
554
|
+
result = {k: x for k, x in r.items() if k != "_meta"}
|
|
555
|
+
top = (result["rows"] or [None])[0]
|
|
556
|
+
total_bits = (f" of {result['total']:,} total" if result.get("total") is not None else "")
|
|
557
|
+
return self._flat_env(
|
|
558
|
+
tool="unsd_sdg_series",
|
|
559
|
+
input={"series": r["params"]["series"], "geo": geo, "time": r["params"]["time"],
|
|
560
|
+
"page": r["params"]["page"], "page_size": r["params"]["page_size"]},
|
|
561
|
+
reason=(f"UNSD SDG series {r['params']['series']} — {result['count']} rows on this "
|
|
562
|
+
f"page{total_bits}"
|
|
563
|
+
+ (f", geo {r['params']['geo']} (local filter)" if r["params"].get("geo") else "")
|
|
564
|
+
+ (f", period {r['params']['time']}" if r["params"].get("time") else "")
|
|
565
|
+
+ (f", top {top['geo_area_name']} {top['period']} = {top['value']}" if top
|
|
566
|
+
else ", zero rows on this page")),
|
|
567
|
+
result=result,
|
|
568
|
+
evidence=[{"quote": f"{x['geo_area_name']} ({x['geo_area_code']}) {x['period']} = {x['value']}",
|
|
569
|
+
"url": f"https://unstats.un.org/SDGAPI/v1/sdg/Series/Data?seriesCode={r['params']['series']}"}
|
|
570
|
+
for x in result["rows"][:5]],
|
|
571
|
+
provider_id="unsd_sdg", metas=[r["_meta"]],
|
|
572
|
+
query={"source": "raw", "upstream": r["params"]},
|
|
573
|
+
)
|