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,163 @@
|
|
|
1
|
+
"""eurostat — Eurostat dissemination API provider (ec.europa.eu; keyless, no registration).
|
|
2
|
+
|
|
3
|
+
One face: table (one Eurostat table x the filters you give). The upstream speaks JSON-stat 2.0:
|
|
4
|
+
values live in a flat index map and the dimensions carry the code -> position map that rebuilds
|
|
5
|
+
the grid.
|
|
6
|
+
|
|
7
|
+
Shapes verified by first-hand live probing 2026-11-02 (tmp/probe-economy/):
|
|
8
|
+
GET /data/{table}?format=JSON&lang=EN&geo=DE&sinceTimePeriod=2022&untilTimePeriod=2023
|
|
9
|
+
-> {version, class, label, source, updated, value:{flatIdx:num}, status:{flatIdx:code},
|
|
10
|
+
id:[dim...], size:[...], dimension:{dim:{label, category:{index:{code:pos}, label:{...}}}}}
|
|
11
|
+
* several values for one dimension = REPEATED parameters (geo=DE&geo=FR -> 2 cells; the "+"
|
|
12
|
+
form is a 400 and the comma form silently yields 0 cells — the upstream "empty, no error"
|
|
13
|
+
trap);
|
|
14
|
+
* sinceTimePeriod / untilTimePeriod bound the time axis (verified: DE 2022-2023 -> 2 cells);
|
|
15
|
+
* a geo that is not in the table yields size 0 / zero cells and NO error (the upstream
|
|
16
|
+
"silent empty" family — a zero-cell answer is reported as zero cells, never as data);
|
|
17
|
+
* the grid is row-major with the LAST dimension fastest (verified: flat 157 -> time 1985, sex F);
|
|
18
|
+
* `status` carries the cell flags (b = break in time series, e = estimated, p = provisional).
|
|
19
|
+
Rate: no official limit published -> polite tier 1 rps / burst 2. TTL: 12h (Eurostat refreshes
|
|
20
|
+
daily and stamps `updated`).
|
|
21
|
+
"""
|
|
22
|
+
from ..errors import SdkError
|
|
23
|
+
|
|
24
|
+
provider = {
|
|
25
|
+
"id": "eurostat",
|
|
26
|
+
"baseUrl": "https://ec.europa.eu/eurostat/api/dissemination/statistics/1.0",
|
|
27
|
+
"rate": {"rps": 1, "burst": 2},
|
|
28
|
+
"ops": {
|
|
29
|
+
"table": {"path": "/data/{table}", "notFound": [404]},
|
|
30
|
+
},
|
|
31
|
+
"ttl": {"table": 12 * 3600},
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
_CELL_CAP = 500_000 # a bare table (no filter) can be tens of millions of cells
|
|
35
|
+
_LABEL_CAP = 500 # per-dimension code->label map: skipped above this size
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _split_values(v):
|
|
39
|
+
"""A CLI value may be a list (repeated flags) or one string; Eurostat takes repeated params."""
|
|
40
|
+
if v is None:
|
|
41
|
+
return []
|
|
42
|
+
if isinstance(v, (list, tuple)):
|
|
43
|
+
items = [str(x).strip() for x in v]
|
|
44
|
+
else:
|
|
45
|
+
items = [x.strip() for x in str(v).split(",")]
|
|
46
|
+
return [x for x in items if x]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _ordered_codes(dim_payload):
|
|
50
|
+
"""category.index is code -> position; the position defines the order."""
|
|
51
|
+
idx = (dim_payload.get("category") or {}).get("index") or {}
|
|
52
|
+
if not idx:
|
|
53
|
+
return []
|
|
54
|
+
ordered = [None] * (max(int(p) for p in idx.values()) + 1)
|
|
55
|
+
for code, pos in idx.items():
|
|
56
|
+
ordered[int(pos)] = code
|
|
57
|
+
return ordered
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _decode(payload):
|
|
61
|
+
"""JSON-stat 2.0 -> (rows, dimension summary). The flat index is row-major, last dim fastest."""
|
|
62
|
+
dims = payload.get("id") or []
|
|
63
|
+
sizes = [int(x) for x in (payload.get("size") or [])]
|
|
64
|
+
dim_payloads = payload.get("dimension") or {}
|
|
65
|
+
codes = [_ordered_codes(dim_payloads.get(d) or {}) for d in dims]
|
|
66
|
+
values = payload.get("value") or {}
|
|
67
|
+
statuses = payload.get("status") or {}
|
|
68
|
+
rows = []
|
|
69
|
+
for flat_s, val in sorted(values.items(), key=lambda kv: int(kv[0])):
|
|
70
|
+
flat = int(flat_s)
|
|
71
|
+
coords = []
|
|
72
|
+
rem = flat
|
|
73
|
+
for i in range(len(dims) - 1, -1, -1):
|
|
74
|
+
sz = sizes[i] if i < len(sizes) else 1
|
|
75
|
+
sz = sz or 1
|
|
76
|
+
coords.insert(0, rem % sz)
|
|
77
|
+
rem //= sz
|
|
78
|
+
row = {}
|
|
79
|
+
for i, d in enumerate(dims):
|
|
80
|
+
pos = coords[i]
|
|
81
|
+
row[d] = codes[i][pos] if pos < len(codes[i]) else None
|
|
82
|
+
row["value"] = val
|
|
83
|
+
st = statuses.get(flat_s)
|
|
84
|
+
if st:
|
|
85
|
+
row["status"] = st
|
|
86
|
+
rows.append(row)
|
|
87
|
+
summary = {}
|
|
88
|
+
for i, d in enumerate(dims):
|
|
89
|
+
dp = dim_payloads.get(d) or {}
|
|
90
|
+
entry = {"label": dp.get("label"), "codes": codes[i]}
|
|
91
|
+
labels = (dp.get("category") or {}).get("label") or {}
|
|
92
|
+
if labels and len(labels) <= _LABEL_CAP:
|
|
93
|
+
entry["labels"] = labels
|
|
94
|
+
summary[d] = entry
|
|
95
|
+
return rows, summary
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def table_raw(table, *, geo=None, time=None, dims=None, since=None, until=None, lang="EN",
|
|
99
|
+
cache=None, no_cache=False):
|
|
100
|
+
"""-> {total, count, rows, params, updated, label, dimensions, _meta}; /data/{table}.
|
|
101
|
+
table = the Eurostat table code (e.g. tec00001, demo_pjan, nama_10_a64); geo / time /
|
|
102
|
+
dims are the upstream dimension filters (several values = repeated parameters); dims is the
|
|
103
|
+
generic carrier for the table-specific dimension ids (age, sex, unit, nace_r2 …) as
|
|
104
|
+
k=v pairs; since / until bound the time axis (upstream sinceTimePeriod / untilTimePeriod);
|
|
105
|
+
lang = the upstream language (EN default).
|
|
106
|
+
Local wall: at least one filter (a bare table is a bulk download), and the cell count must
|
|
107
|
+
stay under the local cap."""
|
|
108
|
+
from ..transport import call_provider
|
|
109
|
+
|
|
110
|
+
tbl = str(table or "").strip()
|
|
111
|
+
if not tbl:
|
|
112
|
+
raise SdkError("usage_error", provider="eurostat",
|
|
113
|
+
detail="eurostat_table requires --table (the Eurostat table code, e.g. tec00001)")
|
|
114
|
+
params = {"format": "JSON", "lang": str(lang or "EN").strip() or "EN", "table": tbl}
|
|
115
|
+
geo_v = _split_values(geo)
|
|
116
|
+
time_v = _split_values(time)
|
|
117
|
+
if geo_v:
|
|
118
|
+
params["geo"] = geo_v
|
|
119
|
+
if time_v:
|
|
120
|
+
params["time"] = time_v
|
|
121
|
+
if since:
|
|
122
|
+
params["sinceTimePeriod"] = str(since).strip()
|
|
123
|
+
if until:
|
|
124
|
+
params["untilTimePeriod"] = str(until).strip()
|
|
125
|
+
dim_items = []
|
|
126
|
+
for raw in (dims or []):
|
|
127
|
+
if isinstance(raw, dict):
|
|
128
|
+
items = list(raw.items())
|
|
129
|
+
else:
|
|
130
|
+
items = [tuple(str(raw).split("=", 1))]
|
|
131
|
+
for k, v in items:
|
|
132
|
+
k = str(k).strip()
|
|
133
|
+
if not k:
|
|
134
|
+
continue
|
|
135
|
+
dim_items.append((k, _split_values(v)))
|
|
136
|
+
if not (geo_v or time_v or dim_items or since or until):
|
|
137
|
+
raise SdkError("usage_error", provider="eurostat",
|
|
138
|
+
detail="eurostat_table requires at least one filter (--geo / --time / --dim / "
|
|
139
|
+
"--since / --until) — a bare table is a bulk download, not a query")
|
|
140
|
+
for k, vals in dim_items:
|
|
141
|
+
if len(vals) == 1:
|
|
142
|
+
params[k] = vals[0]
|
|
143
|
+
else:
|
|
144
|
+
params[k] = vals
|
|
145
|
+
r = call_provider(provider, "table", params, cache=cache, no_cache=no_cache)
|
|
146
|
+
payload = r["data"]
|
|
147
|
+
if not isinstance(payload, dict):
|
|
148
|
+
raise SdkError("upstream_unavailable", provider="eurostat",
|
|
149
|
+
detail=f"Unexpected Eurostat payload shape: {str(payload)[:160]!r}")
|
|
150
|
+
sizes = [int(x) for x in (payload.get("size") or [])]
|
|
151
|
+
cells = 1
|
|
152
|
+
for s in sizes:
|
|
153
|
+
cells *= (s or 0)
|
|
154
|
+
if cells > _CELL_CAP:
|
|
155
|
+
raise SdkError("usage_error", provider="eurostat",
|
|
156
|
+
detail=f"The filtered table still holds {cells:,} cells (local cap "
|
|
157
|
+
f"{_CELL_CAP:,}) — narrow the filters (geo / time / dim)")
|
|
158
|
+
rows, summary = _decode(payload)
|
|
159
|
+
return {"total": cells, "count": len(rows), "rows": rows,
|
|
160
|
+
"label": payload.get("label"), "updated": payload.get("updated"),
|
|
161
|
+
"source": payload.get("source"), "dimensions": summary,
|
|
162
|
+
"params": {k: v for k, v in params.items() if k != "table"},
|
|
163
|
+
"_meta": r}
|