ilostat 0.1.0__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.
- ilostat/__init__.py +21 -0
- ilostat/cache.py +183 -0
- ilostat/data.py +260 -0
- ilostat/dictionary.py +193 -0
- ilostat/filters.py +114 -0
- ilostat/labels.py +217 -0
- ilostat/metadata.py +198 -0
- ilostat/sdmx.py +3291 -0
- ilostat-0.1.0.dist-info/METADATA +763 -0
- ilostat-0.1.0.dist-info/RECORD +13 -0
- ilostat-0.1.0.dist-info/WHEEL +5 -0
- ilostat-0.1.0.dist-info/licenses/LICENSE +25 -0
- ilostat-0.1.0.dist-info/top_level.txt +1 -0
ilostat/__init__.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from importlib.metadata import version
|
|
2
|
+
|
|
3
|
+
from .cache import clean_cache, get_cache_dir
|
|
4
|
+
from .data import get_df
|
|
5
|
+
from .dictionary import get_dataset_dic, get_dic
|
|
6
|
+
from .metadata import get_metadata, get_toc
|
|
7
|
+
from .sdmx import get_sdmx
|
|
8
|
+
|
|
9
|
+
__version__ = version("ilostat")
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"__version__",
|
|
13
|
+
"get_dataset_dic",
|
|
14
|
+
"get_df",
|
|
15
|
+
"get_dic",
|
|
16
|
+
"get_metadata",
|
|
17
|
+
"get_toc",
|
|
18
|
+
"clean_cache",
|
|
19
|
+
"get_cache_dir",
|
|
20
|
+
"get_sdmx",
|
|
21
|
+
]
|
ilostat/cache.py
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from platformdirs import user_cache_dir
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _format_last_update(
|
|
9
|
+
last_update: str,
|
|
10
|
+
) -> str:
|
|
11
|
+
"""
|
|
12
|
+
Convert an ILOSTAT last.update value to a cache timestamp.
|
|
13
|
+
|
|
14
|
+
Example
|
|
15
|
+
-------
|
|
16
|
+
'20/09/2026 07:34:56' -> '20260920T073456'
|
|
17
|
+
"""
|
|
18
|
+
date_part, time_part = last_update.split(" ")
|
|
19
|
+
|
|
20
|
+
day, month, year = date_part.split("/")
|
|
21
|
+
hour, minute, second = time_part.split(":")
|
|
22
|
+
|
|
23
|
+
return f"{year}{month}{day}T{hour}{minute}{second}"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _versioned_cache_filename(
|
|
27
|
+
dataset_id: str,
|
|
28
|
+
last_update: str,
|
|
29
|
+
suffix: str,
|
|
30
|
+
) -> str:
|
|
31
|
+
"""
|
|
32
|
+
Build a versioned cache filename.
|
|
33
|
+
|
|
34
|
+
Examples
|
|
35
|
+
--------
|
|
36
|
+
Dataset:
|
|
37
|
+
UNE_TUNE_SEX_AGE_NB_A-20260930T070804.parquet
|
|
38
|
+
|
|
39
|
+
Dictionary:
|
|
40
|
+
UNE_TUNE_SEX_AGE_NB_A-20260930T070804_dic.json
|
|
41
|
+
"""
|
|
42
|
+
timestamp = _format_last_update(
|
|
43
|
+
last_update
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
f"{dataset_id}-"
|
|
48
|
+
f"{timestamp}"
|
|
49
|
+
f"{suffix}"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _cache_filename(
|
|
54
|
+
dataset_id: str,
|
|
55
|
+
last_update: str,
|
|
56
|
+
) -> str:
|
|
57
|
+
"""
|
|
58
|
+
Build the filename for a cached ILOSTAT dataset.
|
|
59
|
+
"""
|
|
60
|
+
return _versioned_cache_filename(
|
|
61
|
+
dataset_id=dataset_id,
|
|
62
|
+
last_update=last_update,
|
|
63
|
+
suffix=".parquet",
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _remove_old_cache_files(
|
|
68
|
+
cache_dir: Path,
|
|
69
|
+
dataset_id: str,
|
|
70
|
+
keep_file: Path,
|
|
71
|
+
suffix: str = ".parquet",
|
|
72
|
+
) -> None:
|
|
73
|
+
"""
|
|
74
|
+
Remove obsolete cached versions of an ILOSTAT resource.
|
|
75
|
+
|
|
76
|
+
Parameters
|
|
77
|
+
----------
|
|
78
|
+
cache_dir : pathlib.Path
|
|
79
|
+
Segment cache directory.
|
|
80
|
+
|
|
81
|
+
dataset_id : str
|
|
82
|
+
ILOSTAT dataset identifier.
|
|
83
|
+
|
|
84
|
+
keep_file : pathlib.Path
|
|
85
|
+
Current cached file that must be preserved.
|
|
86
|
+
|
|
87
|
+
suffix : str, default ".parquet"
|
|
88
|
+
Resource filename suffix.
|
|
89
|
+
"""
|
|
90
|
+
pattern = (
|
|
91
|
+
f"{dataset_id}-"
|
|
92
|
+
f"*{suffix}"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
for file in cache_dir.glob(
|
|
96
|
+
pattern
|
|
97
|
+
):
|
|
98
|
+
if file != keep_file:
|
|
99
|
+
file.unlink()
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def get_cache_dir(
|
|
103
|
+
cache_dir: str | Path | None = None,
|
|
104
|
+
) -> Path:
|
|
105
|
+
"""
|
|
106
|
+
Return the ILOSTAT cache directory.
|
|
107
|
+
|
|
108
|
+
Parameters
|
|
109
|
+
----------
|
|
110
|
+
cache_dir : str, pathlib.Path or None
|
|
111
|
+
Custom cache directory. If None, use the operating
|
|
112
|
+
system's standard user cache directory.
|
|
113
|
+
|
|
114
|
+
Returns
|
|
115
|
+
-------
|
|
116
|
+
pathlib.Path
|
|
117
|
+
Cache directory.
|
|
118
|
+
"""
|
|
119
|
+
if cache_dir is None:
|
|
120
|
+
path = Path(
|
|
121
|
+
user_cache_dir("ilostat")
|
|
122
|
+
)
|
|
123
|
+
else:
|
|
124
|
+
path = Path(
|
|
125
|
+
cache_dir
|
|
126
|
+
).expanduser()
|
|
127
|
+
|
|
128
|
+
path.mkdir(
|
|
129
|
+
parents=True,
|
|
130
|
+
exist_ok=True,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
return path
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def clean_cache(
|
|
137
|
+
cache_dir: str | Path | None = None,
|
|
138
|
+
) -> None:
|
|
139
|
+
"""
|
|
140
|
+
Remove all files from the ILOSTAT cache.
|
|
141
|
+
|
|
142
|
+
Parameters
|
|
143
|
+
----------
|
|
144
|
+
cache_dir : str, pathlib.Path or None
|
|
145
|
+
ILOSTAT cache directory to clean.
|
|
146
|
+
|
|
147
|
+
If None, clean the default ILOSTAT user cache
|
|
148
|
+
directory.
|
|
149
|
+
|
|
150
|
+
If provided, clean the specified cache directory.
|
|
151
|
+
|
|
152
|
+
Notes
|
|
153
|
+
-----
|
|
154
|
+
The cache directory itself is preserved.
|
|
155
|
+
"""
|
|
156
|
+
path = get_cache_dir(
|
|
157
|
+
cache_dir
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
for file in path.rglob("*"):
|
|
161
|
+
if file.is_file():
|
|
162
|
+
file.unlink()
|
|
163
|
+
|
|
164
|
+
# Remove empty subdirectories, deepest first.
|
|
165
|
+
directories = sorted(
|
|
166
|
+
(
|
|
167
|
+
directory
|
|
168
|
+
for directory in path.rglob("*")
|
|
169
|
+
if directory.is_dir()
|
|
170
|
+
),
|
|
171
|
+
key=lambda directory: len(
|
|
172
|
+
directory.parts
|
|
173
|
+
),
|
|
174
|
+
reverse=True,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
for directory in directories:
|
|
178
|
+
try:
|
|
179
|
+
directory.rmdir()
|
|
180
|
+
except OSError:
|
|
181
|
+
# Keep non-empty or temporarily locked
|
|
182
|
+
# directories.
|
|
183
|
+
pass
|
ilostat/data.py
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
import requests
|
|
7
|
+
|
|
8
|
+
from .cache import (
|
|
9
|
+
_cache_filename,
|
|
10
|
+
_remove_old_cache_files,
|
|
11
|
+
get_cache_dir,
|
|
12
|
+
)
|
|
13
|
+
from .dictionary import get_dataset_dic
|
|
14
|
+
from .filters import apply_filters
|
|
15
|
+
from .labels import add_labels, labels_only
|
|
16
|
+
from .metadata import get_last_update
|
|
17
|
+
|
|
18
|
+
BASE_URL = "https://rplumber.ilo.org/files"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _get_df_single(
|
|
22
|
+
dataset_id: str,
|
|
23
|
+
segment: str = "indicator",
|
|
24
|
+
type: str = "code",
|
|
25
|
+
lang: str = "en",
|
|
26
|
+
filters: dict | None = None,
|
|
27
|
+
best_source: str = "yes",
|
|
28
|
+
fixed: bool = True,
|
|
29
|
+
cache_dir: str | Path | None = None,
|
|
30
|
+
refresh: bool = False,
|
|
31
|
+
) -> pd.DataFrame:
|
|
32
|
+
"""
|
|
33
|
+
Retrieve an ILOSTAT dataset as a pandas DataFrame.
|
|
34
|
+
|
|
35
|
+
Parameters
|
|
36
|
+
----------
|
|
37
|
+
dataset_id : str
|
|
38
|
+
ILOSTAT dataset identifier.
|
|
39
|
+
|
|
40
|
+
segment : str, default "indicator"
|
|
41
|
+
ILOSTAT bulk-data segment.
|
|
42
|
+
|
|
43
|
+
type : {"code", "label", "both"}, default "code"
|
|
44
|
+
Output representation for coded dimensions.
|
|
45
|
+
|
|
46
|
+
"code" returns codes only.
|
|
47
|
+
"both" returns codes and labels.
|
|
48
|
+
"label" returns labelled dimensions.
|
|
49
|
+
|
|
50
|
+
lang : {"en", "fr", "es"}, default "en"
|
|
51
|
+
Language used for labels.
|
|
52
|
+
|
|
53
|
+
filters : dict or None, default None
|
|
54
|
+
Filters applied to the dataset.
|
|
55
|
+
|
|
56
|
+
best_source : {"yes", "no", "all"}, default "yes"
|
|
57
|
+
Select best-source observations.
|
|
58
|
+
|
|
59
|
+
fixed : bool, default True
|
|
60
|
+
If True, treat filter values as literal strings.
|
|
61
|
+
|
|
62
|
+
cache_dir : str, pathlib.Path or None, default None
|
|
63
|
+
Directory used to cache downloaded files.
|
|
64
|
+
|
|
65
|
+
refresh : bool, default False
|
|
66
|
+
If True, refresh the table of contents before checking
|
|
67
|
+
the cached dataset.
|
|
68
|
+
|
|
69
|
+
Returns
|
|
70
|
+
-------
|
|
71
|
+
pandas.DataFrame
|
|
72
|
+
Requested ILOSTAT dataset.
|
|
73
|
+
"""
|
|
74
|
+
type = type.lower()
|
|
75
|
+
|
|
76
|
+
if type not in {"code", "label", "both"}:
|
|
77
|
+
raise ValueError(
|
|
78
|
+
"Invalid type value. "
|
|
79
|
+
"Choose from 'code', 'label', or 'both'."
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
cache_path = get_cache_dir(cache_dir)
|
|
83
|
+
|
|
84
|
+
last_update = get_last_update(
|
|
85
|
+
dataset_id=dataset_id,
|
|
86
|
+
segment=segment,
|
|
87
|
+
cache_dir=cache_dir,
|
|
88
|
+
refresh=refresh,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
if last_update is None:
|
|
92
|
+
raise ValueError(
|
|
93
|
+
f"Dataset '{dataset_id}' was not found "
|
|
94
|
+
f"in the '{segment}' table of contents."
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
filename = _cache_filename(
|
|
98
|
+
dataset_id=dataset_id,
|
|
99
|
+
last_update=last_update,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
segment_dir = cache_path / segment
|
|
103
|
+
segment_dir.mkdir(
|
|
104
|
+
parents=True,
|
|
105
|
+
exist_ok=True,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
dataset_file = segment_dir / filename
|
|
109
|
+
|
|
110
|
+
if not dataset_file.exists():
|
|
111
|
+
url = (
|
|
112
|
+
f"{BASE_URL}/{segment}/"
|
|
113
|
+
f"{dataset_id}.parquet"
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
response = requests.get(
|
|
117
|
+
url,
|
|
118
|
+
timeout=60,
|
|
119
|
+
)
|
|
120
|
+
response.raise_for_status()
|
|
121
|
+
|
|
122
|
+
dataset_file.write_bytes(
|
|
123
|
+
response.content
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
_remove_old_cache_files(
|
|
127
|
+
cache_dir=segment_dir,
|
|
128
|
+
dataset_id=dataset_id,
|
|
129
|
+
keep_file=dataset_file,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
df = pd.read_parquet(dataset_file)
|
|
133
|
+
|
|
134
|
+
best_source = best_source.lower()
|
|
135
|
+
|
|
136
|
+
if best_source not in {"yes", "no", "all"}:
|
|
137
|
+
raise ValueError(
|
|
138
|
+
"Invalid best_source value. "
|
|
139
|
+
"Choose from 'yes', 'no', or 'all'."
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
if "best_source" in df.columns:
|
|
143
|
+
if best_source == "yes":
|
|
144
|
+
df = df.loc[
|
|
145
|
+
df["best_source"] == 1
|
|
146
|
+
]
|
|
147
|
+
elif best_source == "no":
|
|
148
|
+
df = df.loc[
|
|
149
|
+
df["best_source"] == 0
|
|
150
|
+
]
|
|
151
|
+
|
|
152
|
+
df = apply_filters(
|
|
153
|
+
df,
|
|
154
|
+
filters=filters,
|
|
155
|
+
fixed=fixed,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
if type in {"label", "both"}:
|
|
159
|
+
dic = get_dataset_dic(
|
|
160
|
+
dataset_id=dataset_id,
|
|
161
|
+
segment=segment,
|
|
162
|
+
cache_dir=cache_dir,
|
|
163
|
+
refresh=refresh,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
df = add_labels(
|
|
167
|
+
df,
|
|
168
|
+
dic,
|
|
169
|
+
lang=lang,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
if type == "label":
|
|
173
|
+
df = labels_only(df)
|
|
174
|
+
|
|
175
|
+
if best_source == "yes":
|
|
176
|
+
df = df.drop(
|
|
177
|
+
columns=["best_source"],
|
|
178
|
+
errors="ignore",
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
return df
|
|
182
|
+
|
|
183
|
+
def get_df(
|
|
184
|
+
dataset_id: str | list[str] | tuple[str, ...],
|
|
185
|
+
segment: str = "indicator",
|
|
186
|
+
type: str = "code",
|
|
187
|
+
lang: str = "en",
|
|
188
|
+
filters: dict | None = None,
|
|
189
|
+
best_source: str = "yes",
|
|
190
|
+
fixed: bool = True,
|
|
191
|
+
cache_dir: str | Path | None = None,
|
|
192
|
+
refresh: bool = False,
|
|
193
|
+
) -> pd.DataFrame:
|
|
194
|
+
"""
|
|
195
|
+
Retrieve one or more ILOSTAT datasets.
|
|
196
|
+
|
|
197
|
+
Parameters
|
|
198
|
+
----------
|
|
199
|
+
dataset_id : str, list of str, or tuple of str
|
|
200
|
+
One or more ILOSTAT dataset identifiers.
|
|
201
|
+
|
|
202
|
+
segment : str, default "indicator"
|
|
203
|
+
ILOSTAT bulk-data segment.
|
|
204
|
+
|
|
205
|
+
type : {"code", "label", "both"}, default "code"
|
|
206
|
+
Output representation for coded dimensions.
|
|
207
|
+
|
|
208
|
+
lang : {"en", "fr", "es"}, default "en"
|
|
209
|
+
Language used for labels.
|
|
210
|
+
|
|
211
|
+
filters : dict or None, default None
|
|
212
|
+
Filters applied to each dataset.
|
|
213
|
+
|
|
214
|
+
best_source : {"yes", "no", "all"}, default "yes"
|
|
215
|
+
Select best-source observations.
|
|
216
|
+
|
|
217
|
+
fixed : bool, default True
|
|
218
|
+
If True, treat filter values as literal strings.
|
|
219
|
+
|
|
220
|
+
cache_dir : str, pathlib.Path or None, default None
|
|
221
|
+
Directory used to cache downloaded files.
|
|
222
|
+
|
|
223
|
+
refresh : bool, default False
|
|
224
|
+
If True, refresh metadata before checking cached data.
|
|
225
|
+
|
|
226
|
+
Returns
|
|
227
|
+
-------
|
|
228
|
+
pandas.DataFrame
|
|
229
|
+
Requested ILOSTAT data.
|
|
230
|
+
"""
|
|
231
|
+
if isinstance(dataset_id, str):
|
|
232
|
+
dataset_ids = [dataset_id]
|
|
233
|
+
else:
|
|
234
|
+
dataset_ids = list(dataset_id)
|
|
235
|
+
|
|
236
|
+
if not dataset_ids:
|
|
237
|
+
raise ValueError(
|
|
238
|
+
"dataset_id must contain at least one dataset identifier."
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
frames = [
|
|
242
|
+
_get_df_single(
|
|
243
|
+
dataset_id=current_id,
|
|
244
|
+
segment=segment,
|
|
245
|
+
type=type,
|
|
246
|
+
lang=lang,
|
|
247
|
+
filters=filters,
|
|
248
|
+
best_source=best_source,
|
|
249
|
+
fixed=fixed,
|
|
250
|
+
cache_dir=cache_dir,
|
|
251
|
+
refresh=refresh,
|
|
252
|
+
)
|
|
253
|
+
for current_id in dataset_ids
|
|
254
|
+
]
|
|
255
|
+
|
|
256
|
+
return pd.concat(
|
|
257
|
+
frames,
|
|
258
|
+
ignore_index=True,
|
|
259
|
+
sort=False,
|
|
260
|
+
)
|
ilostat/dictionary.py
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
import pandas as pd
|
|
8
|
+
import requests
|
|
9
|
+
|
|
10
|
+
from .cache import (
|
|
11
|
+
_remove_old_cache_files,
|
|
12
|
+
_versioned_cache_filename,
|
|
13
|
+
get_cache_dir,
|
|
14
|
+
)
|
|
15
|
+
from .metadata import get_last_update
|
|
16
|
+
|
|
17
|
+
BASE_URL = "https://rplumber.ilo.org/files/dic"
|
|
18
|
+
DATA_BASE_URL = "https://rplumber.ilo.org/files"
|
|
19
|
+
|
|
20
|
+
VALID_LANGUAGES = {"en", "fr", "es"}
|
|
21
|
+
|
|
22
|
+
_REFRESHED_DICS: set[tuple[str, str, str]] = set()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_dic(
|
|
26
|
+
dic: str,
|
|
27
|
+
lang: str = "en",
|
|
28
|
+
cache_dir: str | Path | None = None,
|
|
29
|
+
refresh: bool = False,
|
|
30
|
+
) -> pd.DataFrame:
|
|
31
|
+
"""
|
|
32
|
+
Retrieve an ILOSTAT dictionary.
|
|
33
|
+
|
|
34
|
+
Parameters
|
|
35
|
+
----------
|
|
36
|
+
dic : str
|
|
37
|
+
Dictionary name, for example "ref_area", "sex",
|
|
38
|
+
or "classif_type".
|
|
39
|
+
|
|
40
|
+
lang : {"en", "fr", "es"}, default "en"
|
|
41
|
+
Dictionary language.
|
|
42
|
+
|
|
43
|
+
cache_dir : str, pathlib.Path or None, default None
|
|
44
|
+
Directory used to cache downloaded dictionaries.
|
|
45
|
+
|
|
46
|
+
refresh : bool, default False
|
|
47
|
+
If True, force a new download of the dictionary.
|
|
48
|
+
|
|
49
|
+
Returns
|
|
50
|
+
-------
|
|
51
|
+
pandas.DataFrame
|
|
52
|
+
Requested ILOSTAT dictionary.
|
|
53
|
+
"""
|
|
54
|
+
lang = lang.lower()
|
|
55
|
+
|
|
56
|
+
if lang not in VALID_LANGUAGES:
|
|
57
|
+
raise ValueError(
|
|
58
|
+
f"Unsupported language '{lang}'. "
|
|
59
|
+
f"Choose from {sorted(VALID_LANGUAGES)}."
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
cache_path = get_cache_dir(cache_dir)
|
|
63
|
+
|
|
64
|
+
dic_dir = cache_path / "dic"
|
|
65
|
+
dic_dir.mkdir(
|
|
66
|
+
parents=True,
|
|
67
|
+
exist_ok=True,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
dic_file = dic_dir / f"{dic}_{lang}.parquet"
|
|
71
|
+
|
|
72
|
+
cache_key = (
|
|
73
|
+
str(cache_path.resolve()),
|
|
74
|
+
dic,
|
|
75
|
+
lang,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
should_download = (
|
|
79
|
+
refresh
|
|
80
|
+
or cache_key not in _REFRESHED_DICS
|
|
81
|
+
or not dic_file.exists()
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
if should_download:
|
|
85
|
+
url = (
|
|
86
|
+
f"{BASE_URL}/"
|
|
87
|
+
f"{dic}_{lang}.parquet"
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
response = requests.get(
|
|
91
|
+
url,
|
|
92
|
+
timeout=60,
|
|
93
|
+
)
|
|
94
|
+
response.raise_for_status()
|
|
95
|
+
|
|
96
|
+
dic_file.write_bytes(
|
|
97
|
+
response.content
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
_REFRESHED_DICS.add(cache_key)
|
|
101
|
+
|
|
102
|
+
return pd.read_parquet(dic_file)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def get_dataset_dic(
|
|
106
|
+
dataset_id: str,
|
|
107
|
+
segment: str = "indicator",
|
|
108
|
+
cache_dir: str | Path | None = None,
|
|
109
|
+
refresh: bool = False,
|
|
110
|
+
) -> dict[str, Any]:
|
|
111
|
+
"""
|
|
112
|
+
Retrieve the dataset-specific ILOSTAT dictionary.
|
|
113
|
+
|
|
114
|
+
The cached dictionary is versioned using the dataset
|
|
115
|
+
last.update value from the table of contents.
|
|
116
|
+
|
|
117
|
+
Parameters
|
|
118
|
+
----------
|
|
119
|
+
dataset_id : str
|
|
120
|
+
ILOSTAT dataset identifier.
|
|
121
|
+
|
|
122
|
+
segment : str, default "indicator"
|
|
123
|
+
ILOSTAT data segment.
|
|
124
|
+
|
|
125
|
+
cache_dir : str, pathlib.Path or None, default None
|
|
126
|
+
Directory used to cache downloaded files.
|
|
127
|
+
|
|
128
|
+
refresh : bool, default False
|
|
129
|
+
If True, refresh the table of contents before checking
|
|
130
|
+
the cached dictionary.
|
|
131
|
+
|
|
132
|
+
Returns
|
|
133
|
+
-------
|
|
134
|
+
dict
|
|
135
|
+
Dataset-specific dictionary.
|
|
136
|
+
"""
|
|
137
|
+
cache_path = get_cache_dir(cache_dir)
|
|
138
|
+
|
|
139
|
+
last_update = get_last_update(
|
|
140
|
+
dataset_id=dataset_id,
|
|
141
|
+
segment=segment,
|
|
142
|
+
cache_dir=cache_dir,
|
|
143
|
+
refresh=refresh,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
if last_update is None:
|
|
147
|
+
raise ValueError(
|
|
148
|
+
f"Dataset '{dataset_id}' was not found "
|
|
149
|
+
f"in the '{segment}' table of contents."
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
segment_dir = cache_path / segment
|
|
153
|
+
segment_dir.mkdir(
|
|
154
|
+
parents=True,
|
|
155
|
+
exist_ok=True,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
filename = _versioned_cache_filename(
|
|
159
|
+
dataset_id=dataset_id,
|
|
160
|
+
last_update=last_update,
|
|
161
|
+
suffix="_dic.json",
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
dic_file = segment_dir / filename
|
|
165
|
+
|
|
166
|
+
if not dic_file.exists():
|
|
167
|
+
url = (
|
|
168
|
+
f"{DATA_BASE_URL}/{segment}/"
|
|
169
|
+
f"{dataset_id}_dic.json"
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
response = requests.get(
|
|
173
|
+
url,
|
|
174
|
+
timeout=60,
|
|
175
|
+
)
|
|
176
|
+
response.raise_for_status()
|
|
177
|
+
|
|
178
|
+
dic_file.write_bytes(
|
|
179
|
+
response.content
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
_remove_old_cache_files(
|
|
183
|
+
cache_dir=segment_dir,
|
|
184
|
+
dataset_id=dataset_id,
|
|
185
|
+
keep_file=dic_file,
|
|
186
|
+
suffix="_dic.json",
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
with dic_file.open(
|
|
190
|
+
"r",
|
|
191
|
+
encoding="utf-8",
|
|
192
|
+
) as file:
|
|
193
|
+
return json.load(file)
|