oda-data 2.6.0__tar.gz → 2.7.0__tar.gz
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.
- {oda_data-2.6.0 → oda_data-2.7.0}/PKG-INFO +28 -10
- {oda_data-2.6.0 → oda_data-2.7.0}/README.md +25 -7
- {oda_data-2.6.0 → oda_data-2.7.0}/pyproject.toml +100 -6
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/__init__.py +4 -3
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/api/oecd.py +12 -10
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/api/representations.py +2 -2
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/api/sources.py +79 -51
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/cache/api.py +14 -10
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/cache/types.py +6 -1
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/clean_data/channels.py +8 -5
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/clean_data/common.py +2 -2
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/clean_data/validation.py +19 -8
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/config.py +4 -2
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/crs/common.py +6 -5
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/crs/crs_functions.py +1 -1
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/crs/oecd.py +25 -7
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/crs/one.py +1 -1
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/dac1/common.py +3 -3
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/dac1/dac1_indicators.json +87 -51
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/dac1/oecd.py +1 -1
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/dac1/one.py +1 -1
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/dac2a/oecd.py +1 -1
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/dac2a/one.py +1 -1
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/indicator.py +1 -1
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/research/eu.py +10 -3
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/research/policy_markers.py +2 -2
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/research/sector_imputations.py +1 -1
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/oda_data_indicators.csv +19 -17
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/settings/flow_types.json +3 -1
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/settings/provider_groupings.json +2 -2
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/settings/recipient_groupings.json +1 -1
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/tools/cache.py +12 -12
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/tools/compatibility.py +7 -5
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/tools/gni.py +2 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/tools/names/create_mapping.py +4 -4
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/tools/names/crs_names.json +4 -5
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/tools/names/dac1_names.json +1 -1
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/tools/names/dac2a_names.json +3 -3
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/tools/sector_lists.py +2 -2
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/api/__init__.py +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/api/constants.py +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/cache/__init__.py +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/cache/_migrate.py +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/cache/config.py +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/clean_data/__init__.py +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/clean_data/crs_channel_mapping.csv +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/clean_data/dtypes.py +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/clean_data/schema.py +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/__init__.py +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/common.py +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/crs/__init__.py +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/crs/crs_indicators.json +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/dac1/__init__.py +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/dac1/dac1_functions.py +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/dac2a/__init__.py +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/dac2a/common.py +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/dac2a/dac2a_functions.py +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/dac2a/dac2a_indicators.json +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/research/__init__.py +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/logger.py +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/settings/crs_flow_types.json +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/settings/crs_modalities.json +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/settings/crs_policy_markers.json +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/settings/crs_purpose.json +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/settings/crs_type_of_finance.json +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/settings/dac1_aid_flow_types.json +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/settings/dac2a_aid_type.json +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/settings/indicators.json +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/settings/key_columns.json +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/settings/v1_indicators_mapping.json +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/tools/__init__.py +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/tools/groupings.py +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/tools/names/__init__.py +0 -0
- {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/tools/names/add.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
|
-
Name:
|
|
3
|
-
Version: 2.
|
|
2
|
+
Name: oda_data
|
|
3
|
+
Version: 2.7.0
|
|
4
4
|
Summary: A python package to work with Official Development Assistance data from the OECD DAC.
|
|
5
5
|
Author: Jorge Rivera, Miguel Haro Ruiz
|
|
6
6
|
Author-email: Jorge Rivera <jorge.rivera@one.org>, Miguel Haro Ruiz <miguel.haroruiz@one.org>
|
|
@@ -25,9 +25,9 @@ Requires-Dist: pydeflate>=2.3.3
|
|
|
25
25
|
Requires-Dist: reader>=3.18
|
|
26
26
|
Requires-Dist: thefuzz>=0.22.1
|
|
27
27
|
Requires-Python: >=3.11
|
|
28
|
-
Project-URL: Documentation, https://github.com/ONEcampaign/oda_data_package
|
|
29
28
|
Project-URL: Homepage, https://github.com/ONEcampaign/oda_data_package
|
|
30
29
|
Project-URL: Repository, https://github.com/ONEcampaign/oda_data_package
|
|
30
|
+
Project-URL: Documentation, https://github.com/ONEcampaign/oda_data_package
|
|
31
31
|
Description-Content-Type: text/markdown
|
|
32
32
|
|
|
33
33
|
[](https://pypi.org/project/oda_data/)
|
|
@@ -39,6 +39,7 @@ Description-Content-Type: text/markdown
|
|
|
39
39
|
[](https://pepy.tech/project/oda-data)
|
|
40
40
|
|
|
41
41
|
# The ODA Data Package
|
|
42
|
+
|
|
42
43
|
This is a Python package designed for accessing, processing, and analyzing Official Development Assistance (ODA) data from OECD DAC. With an intuitive API, it simplifies common tasks like retrieving ODA indicators, converting currencies, filtering data, and handling bulk downloads.
|
|
43
44
|
|
|
44
45
|
**Note: This is the new version of the package (V2). This release includes many breaking changes and previous workflows
|
|
@@ -71,6 +72,7 @@ The package is compatible with Python 3.11 and above.
|
|
|
71
72
|
Most users can get the data they need by using the `OECDClient` class.
|
|
72
73
|
|
|
73
74
|
An object of this class can handle:
|
|
75
|
+
|
|
74
76
|
- getting data for specific indicators (one or more)
|
|
75
77
|
- filtering the data for specific donors, recipients(if relevant), years.
|
|
76
78
|
- returning the data in a variety of currency/prices combinations.
|
|
@@ -160,6 +162,7 @@ OECDClient.export_available_indicators(export_folder="path/to/folder/")
|
|
|
160
162
|
```
|
|
161
163
|
|
|
162
164
|
### Providers
|
|
165
|
+
|
|
163
166
|
You can get a dictionary with all available providers, with their code and name, by using the `.available_providers()` method:
|
|
164
167
|
|
|
165
168
|
```python
|
|
@@ -169,6 +172,7 @@ providers = OECDClient.available_providers()
|
|
|
169
172
|
```
|
|
170
173
|
|
|
171
174
|
### Recipients
|
|
175
|
+
|
|
172
176
|
You can get a dictionary with all available recipients, with their code and name, by using the `.available_recipients()` method:
|
|
173
177
|
|
|
174
178
|
```python
|
|
@@ -178,6 +182,7 @@ recipients = OECDClient.available_recipients()
|
|
|
178
182
|
```
|
|
179
183
|
|
|
180
184
|
### Currencies
|
|
185
|
+
|
|
181
186
|
You can get a dictionary with all available currencies, with their code and name, by using the `.available_currencies()` method:
|
|
182
187
|
|
|
183
188
|
```python
|
|
@@ -191,6 +196,7 @@ currencies = OECDClient.available_currencies()
|
|
|
191
196
|
Different measures are available depending on the data source. Here's what each source supports:
|
|
192
197
|
|
|
193
198
|
**DAC1 Measures:**
|
|
199
|
+
|
|
194
200
|
- `commitment` - Total commitments
|
|
195
201
|
- `commitment_grant` - Grant commitments only
|
|
196
202
|
- `commitment_non_grant` - Non-grant commitments
|
|
@@ -202,10 +208,12 @@ Different measures are available depending on the data source. Here's what each
|
|
|
202
208
|
- `received` - Amounts received
|
|
203
209
|
|
|
204
210
|
**DAC2A Measures:**
|
|
211
|
+
|
|
205
212
|
- `net_disbursement` - Net disbursements (default)
|
|
206
213
|
- `gross_disbursement` - Gross disbursements
|
|
207
214
|
|
|
208
215
|
**CRS Measures:**
|
|
216
|
+
|
|
209
217
|
- `commitment` - Commitment amounts
|
|
210
218
|
- `grant_equivalent` - Grant equivalent values
|
|
211
219
|
- `gross_disbursement` - Gross disbursement amounts
|
|
@@ -215,16 +223,20 @@ Different measures are available depending on the data source. Here's what each
|
|
|
215
223
|
- `export_credit` - Export credits
|
|
216
224
|
|
|
217
225
|
**MultiSystem Measures:**
|
|
226
|
+
|
|
218
227
|
- `commitment` - Commitments
|
|
219
228
|
- `gross_disbursement` - Gross disbursements
|
|
220
229
|
|
|
221
230
|
## Accessing the data
|
|
231
|
+
|
|
222
232
|
For more advanced users, you can use this package to get and work with different tables and databases from the OECD DAC.
|
|
223
233
|
|
|
224
234
|
### DAC 1
|
|
235
|
+
|
|
225
236
|
The `DAC1Data` class can retrieve and return DAC 1 data.
|
|
226
237
|
|
|
227
238
|
You can optionally specify:
|
|
239
|
+
|
|
228
240
|
- years: to filter the data for specific years
|
|
229
241
|
- providers: to filter the data for specific providers (based on their code).
|
|
230
242
|
- indicators: to filter the data for a specific indicator (for DAC1 this means AIDTYPE_CODE)
|
|
@@ -232,6 +244,7 @@ You can optionally specify:
|
|
|
232
244
|
Not specifying any or all of these parameters will return all the available data.
|
|
233
245
|
|
|
234
246
|
After instantiating the object, you can call:
|
|
247
|
+
|
|
235
248
|
- `download`: to download the data from the data-explorer API or the bulk download service. This method returns a DataFrame.
|
|
236
249
|
- `read`: to read data as stored in the specified folder. If the data is not available, it will be downloaded.
|
|
237
250
|
|
|
@@ -271,11 +284,12 @@ data = dac1.read(
|
|
|
271
284
|
)
|
|
272
285
|
```
|
|
273
286
|
|
|
274
|
-
|
|
275
287
|
### DAC 2A
|
|
288
|
+
|
|
276
289
|
The `DAC2AData` class can retrieve and return DAC2a data.
|
|
277
290
|
|
|
278
291
|
You can optionally specify:
|
|
292
|
+
|
|
279
293
|
- years: to filter the data for specific years
|
|
280
294
|
- providers: to filter the data for specific providers (based on their code).
|
|
281
295
|
- recipients: to filter the data for specific recipients (based on their code).
|
|
@@ -284,6 +298,7 @@ You can optionally specify:
|
|
|
284
298
|
Not specifying any or all of these parameters will return all the available data.
|
|
285
299
|
|
|
286
300
|
After instantiating the object, you can call:
|
|
301
|
+
|
|
287
302
|
- `download`: to download the data from the data-explorer API or the bulk download service. This method returns a DataFrame.
|
|
288
303
|
- `read`: to read data as stored in the specified folder. If the data is not available, it will be downloaded.
|
|
289
304
|
|
|
@@ -333,9 +348,11 @@ data = dac2a.read(
|
|
|
333
348
|
```
|
|
334
349
|
|
|
335
350
|
### CRS
|
|
351
|
+
|
|
336
352
|
The `CRSData` class can retrieve and return CRS data.
|
|
337
353
|
|
|
338
354
|
You can optionally specify:
|
|
355
|
+
|
|
339
356
|
- years: to filter the data for specific years
|
|
340
357
|
- providers: to filter the data for specific providers (based on their code).
|
|
341
358
|
- recipients: to filter the data for specific recipients (based on their code).
|
|
@@ -359,11 +376,12 @@ crs = CRSData(years=range(2010, 2024))
|
|
|
359
376
|
data = crs.read(using_bulk_download=True)
|
|
360
377
|
```
|
|
361
378
|
|
|
362
|
-
|
|
363
379
|
### MultiSystem
|
|
380
|
+
|
|
364
381
|
The `MultiSystemData` class can retrieve and return Providers Total Use of the Multilateral System data.
|
|
365
382
|
|
|
366
383
|
You can optionally specify:
|
|
384
|
+
|
|
367
385
|
- years: to filter the data for specific years
|
|
368
386
|
- providers: to filter the data for specific providers (based on their code).
|
|
369
387
|
- recipients: to filter the data for specific recipients (based on their code).
|
|
@@ -376,7 +394,6 @@ You can refer to the examples above for DAC1 and DAC2a to understand the usage o
|
|
|
376
394
|
**Note**: given the speed and rate-limiting of the dac-explorer APIs, consider using the bulk option
|
|
377
395
|
unless you're looking for something very specific.
|
|
378
396
|
|
|
379
|
-
|
|
380
397
|
## Utility Functions
|
|
381
398
|
|
|
382
399
|
The package provides several utility functions to enhance and enrich your ODA data analysis:
|
|
@@ -543,11 +560,12 @@ data = aiddata.read()
|
|
|
543
560
|
## Key features
|
|
544
561
|
|
|
545
562
|
- **Speed up analysis** - The package handles downloading, cleaning and loading all the data, so you can focus on the
|
|
546
|
-
analysis.
|
|
563
|
+
analysis.
|
|
547
564
|
- **Get data in the currency and prices you need** - ODA data is only available in US dollars (current or constant
|
|
548
|
-
prices) and local currency units (current prices). The package allows you to view the data in US dollars, Euros,
|
|
549
|
-
British Pounds and Canadian dollars, in both current and constant prices. We can add any other DAC currency if you
|
|
550
|
-
request it via the [issues page](https://github.com/ONEcampaign/oda_data_package/issues)
|
|
565
|
+
prices) and local currency units (current prices). The package allows you to view the data in US dollars, Euros,
|
|
566
|
+
British Pounds and Canadian dollars, in both current and constant prices. We can add any other DAC currency if you
|
|
567
|
+
request it via the [issues page](https://github.com/ONEcampaign/oda_data_package/issues)
|
|
551
568
|
|
|
552
569
|
## Contributing
|
|
570
|
+
|
|
553
571
|
Interested in contributing to the package? Please reach out.
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
[](https://pepy.tech/project/oda-data)
|
|
8
8
|
|
|
9
9
|
# The ODA Data Package
|
|
10
|
+
|
|
10
11
|
This is a Python package designed for accessing, processing, and analyzing Official Development Assistance (ODA) data from OECD DAC. With an intuitive API, it simplifies common tasks like retrieving ODA indicators, converting currencies, filtering data, and handling bulk downloads.
|
|
11
12
|
|
|
12
13
|
**Note: This is the new version of the package (V2). This release includes many breaking changes and previous workflows
|
|
@@ -39,6 +40,7 @@ The package is compatible with Python 3.11 and above.
|
|
|
39
40
|
Most users can get the data they need by using the `OECDClient` class.
|
|
40
41
|
|
|
41
42
|
An object of this class can handle:
|
|
43
|
+
|
|
42
44
|
- getting data for specific indicators (one or more)
|
|
43
45
|
- filtering the data for specific donors, recipients(if relevant), years.
|
|
44
46
|
- returning the data in a variety of currency/prices combinations.
|
|
@@ -128,6 +130,7 @@ OECDClient.export_available_indicators(export_folder="path/to/folder/")
|
|
|
128
130
|
```
|
|
129
131
|
|
|
130
132
|
### Providers
|
|
133
|
+
|
|
131
134
|
You can get a dictionary with all available providers, with their code and name, by using the `.available_providers()` method:
|
|
132
135
|
|
|
133
136
|
```python
|
|
@@ -137,6 +140,7 @@ providers = OECDClient.available_providers()
|
|
|
137
140
|
```
|
|
138
141
|
|
|
139
142
|
### Recipients
|
|
143
|
+
|
|
140
144
|
You can get a dictionary with all available recipients, with their code and name, by using the `.available_recipients()` method:
|
|
141
145
|
|
|
142
146
|
```python
|
|
@@ -146,6 +150,7 @@ recipients = OECDClient.available_recipients()
|
|
|
146
150
|
```
|
|
147
151
|
|
|
148
152
|
### Currencies
|
|
153
|
+
|
|
149
154
|
You can get a dictionary with all available currencies, with their code and name, by using the `.available_currencies()` method:
|
|
150
155
|
|
|
151
156
|
```python
|
|
@@ -159,6 +164,7 @@ currencies = OECDClient.available_currencies()
|
|
|
159
164
|
Different measures are available depending on the data source. Here's what each source supports:
|
|
160
165
|
|
|
161
166
|
**DAC1 Measures:**
|
|
167
|
+
|
|
162
168
|
- `commitment` - Total commitments
|
|
163
169
|
- `commitment_grant` - Grant commitments only
|
|
164
170
|
- `commitment_non_grant` - Non-grant commitments
|
|
@@ -170,10 +176,12 @@ Different measures are available depending on the data source. Here's what each
|
|
|
170
176
|
- `received` - Amounts received
|
|
171
177
|
|
|
172
178
|
**DAC2A Measures:**
|
|
179
|
+
|
|
173
180
|
- `net_disbursement` - Net disbursements (default)
|
|
174
181
|
- `gross_disbursement` - Gross disbursements
|
|
175
182
|
|
|
176
183
|
**CRS Measures:**
|
|
184
|
+
|
|
177
185
|
- `commitment` - Commitment amounts
|
|
178
186
|
- `grant_equivalent` - Grant equivalent values
|
|
179
187
|
- `gross_disbursement` - Gross disbursement amounts
|
|
@@ -183,16 +191,20 @@ Different measures are available depending on the data source. Here's what each
|
|
|
183
191
|
- `export_credit` - Export credits
|
|
184
192
|
|
|
185
193
|
**MultiSystem Measures:**
|
|
194
|
+
|
|
186
195
|
- `commitment` - Commitments
|
|
187
196
|
- `gross_disbursement` - Gross disbursements
|
|
188
197
|
|
|
189
198
|
## Accessing the data
|
|
199
|
+
|
|
190
200
|
For more advanced users, you can use this package to get and work with different tables and databases from the OECD DAC.
|
|
191
201
|
|
|
192
202
|
### DAC 1
|
|
203
|
+
|
|
193
204
|
The `DAC1Data` class can retrieve and return DAC 1 data.
|
|
194
205
|
|
|
195
206
|
You can optionally specify:
|
|
207
|
+
|
|
196
208
|
- years: to filter the data for specific years
|
|
197
209
|
- providers: to filter the data for specific providers (based on their code).
|
|
198
210
|
- indicators: to filter the data for a specific indicator (for DAC1 this means AIDTYPE_CODE)
|
|
@@ -200,6 +212,7 @@ You can optionally specify:
|
|
|
200
212
|
Not specifying any or all of these parameters will return all the available data.
|
|
201
213
|
|
|
202
214
|
After instantiating the object, you can call:
|
|
215
|
+
|
|
203
216
|
- `download`: to download the data from the data-explorer API or the bulk download service. This method returns a DataFrame.
|
|
204
217
|
- `read`: to read data as stored in the specified folder. If the data is not available, it will be downloaded.
|
|
205
218
|
|
|
@@ -239,11 +252,12 @@ data = dac1.read(
|
|
|
239
252
|
)
|
|
240
253
|
```
|
|
241
254
|
|
|
242
|
-
|
|
243
255
|
### DAC 2A
|
|
256
|
+
|
|
244
257
|
The `DAC2AData` class can retrieve and return DAC2a data.
|
|
245
258
|
|
|
246
259
|
You can optionally specify:
|
|
260
|
+
|
|
247
261
|
- years: to filter the data for specific years
|
|
248
262
|
- providers: to filter the data for specific providers (based on their code).
|
|
249
263
|
- recipients: to filter the data for specific recipients (based on their code).
|
|
@@ -252,6 +266,7 @@ You can optionally specify:
|
|
|
252
266
|
Not specifying any or all of these parameters will return all the available data.
|
|
253
267
|
|
|
254
268
|
After instantiating the object, you can call:
|
|
269
|
+
|
|
255
270
|
- `download`: to download the data from the data-explorer API or the bulk download service. This method returns a DataFrame.
|
|
256
271
|
- `read`: to read data as stored in the specified folder. If the data is not available, it will be downloaded.
|
|
257
272
|
|
|
@@ -301,9 +316,11 @@ data = dac2a.read(
|
|
|
301
316
|
```
|
|
302
317
|
|
|
303
318
|
### CRS
|
|
319
|
+
|
|
304
320
|
The `CRSData` class can retrieve and return CRS data.
|
|
305
321
|
|
|
306
322
|
You can optionally specify:
|
|
323
|
+
|
|
307
324
|
- years: to filter the data for specific years
|
|
308
325
|
- providers: to filter the data for specific providers (based on their code).
|
|
309
326
|
- recipients: to filter the data for specific recipients (based on their code).
|
|
@@ -327,11 +344,12 @@ crs = CRSData(years=range(2010, 2024))
|
|
|
327
344
|
data = crs.read(using_bulk_download=True)
|
|
328
345
|
```
|
|
329
346
|
|
|
330
|
-
|
|
331
347
|
### MultiSystem
|
|
348
|
+
|
|
332
349
|
The `MultiSystemData` class can retrieve and return Providers Total Use of the Multilateral System data.
|
|
333
350
|
|
|
334
351
|
You can optionally specify:
|
|
352
|
+
|
|
335
353
|
- years: to filter the data for specific years
|
|
336
354
|
- providers: to filter the data for specific providers (based on their code).
|
|
337
355
|
- recipients: to filter the data for specific recipients (based on their code).
|
|
@@ -344,7 +362,6 @@ You can refer to the examples above for DAC1 and DAC2a to understand the usage o
|
|
|
344
362
|
**Note**: given the speed and rate-limiting of the dac-explorer APIs, consider using the bulk option
|
|
345
363
|
unless you're looking for something very specific.
|
|
346
364
|
|
|
347
|
-
|
|
348
365
|
## Utility Functions
|
|
349
366
|
|
|
350
367
|
The package provides several utility functions to enhance and enrich your ODA data analysis:
|
|
@@ -511,11 +528,12 @@ data = aiddata.read()
|
|
|
511
528
|
## Key features
|
|
512
529
|
|
|
513
530
|
- **Speed up analysis** - The package handles downloading, cleaning and loading all the data, so you can focus on the
|
|
514
|
-
analysis.
|
|
531
|
+
analysis.
|
|
515
532
|
- **Get data in the currency and prices you need** - ODA data is only available in US dollars (current or constant
|
|
516
|
-
prices) and local currency units (current prices). The package allows you to view the data in US dollars, Euros,
|
|
517
|
-
British Pounds and Canadian dollars, in both current and constant prices. We can add any other DAC currency if you
|
|
518
|
-
request it via the [issues page](https://github.com/ONEcampaign/oda_data_package/issues)
|
|
533
|
+
prices) and local currency units (current prices). The package allows you to view the data in US dollars, Euros,
|
|
534
|
+
British Pounds and Canadian dollars, in both current and constant prices. We can add any other DAC currency if you
|
|
535
|
+
request it via the [issues page](https://github.com/ONEcampaign/oda_data_package/issues)
|
|
519
536
|
|
|
520
537
|
## Contributing
|
|
538
|
+
|
|
521
539
|
Interested in contributing to the package? Please reach out.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "oda_data"
|
|
3
|
-
version = "2.
|
|
3
|
+
version = "2.7.0"
|
|
4
4
|
description = "A python package to work with Official Development Assistance data from the OECD DAC."
|
|
5
5
|
requires-python = ">=3.11"
|
|
6
6
|
|
|
@@ -42,14 +42,14 @@ Repository = "https://github.com/ONEcampaign/oda_data_package"
|
|
|
42
42
|
Documentation = "https://github.com/ONEcampaign/oda_data_package"
|
|
43
43
|
|
|
44
44
|
[build-system]
|
|
45
|
-
requires = ["uv_build>=0.8
|
|
45
|
+
requires = ["uv_build>=0.11.8,<0.12"]
|
|
46
46
|
build-backend = "uv_build"
|
|
47
47
|
|
|
48
48
|
[dependency-groups]
|
|
49
49
|
dev = [
|
|
50
|
-
"uv-build>=0.9.17",
|
|
51
50
|
"pre-commit>=4.5.0",
|
|
52
51
|
"ruff>=0.14.9",
|
|
52
|
+
"ty>=0.0.33",
|
|
53
53
|
"mkdocs-material>=9.7.0",
|
|
54
54
|
]
|
|
55
55
|
test = [
|
|
@@ -67,6 +67,9 @@ line-length = 88
|
|
|
67
67
|
# Target Python 3.11+
|
|
68
68
|
target-version = "py311"
|
|
69
69
|
|
|
70
|
+
# Lint/format notebooks too
|
|
71
|
+
extend-include = ["*.ipynb"]
|
|
72
|
+
|
|
70
73
|
# Exclude common directories
|
|
71
74
|
exclude = [
|
|
72
75
|
".git",
|
|
@@ -94,14 +97,21 @@ select = [
|
|
|
94
97
|
"SIM", # flake8-simplify
|
|
95
98
|
"PL", # pylint
|
|
96
99
|
"RUF", # ruff-specific rules
|
|
100
|
+
"ERA", # eradicate (commented-out code)
|
|
101
|
+
"PERF", # perflint
|
|
102
|
+
"ANN", # flake8-annotations (require type hints on public APIs)
|
|
97
103
|
]
|
|
98
104
|
|
|
99
105
|
# Ignore specific rules that might be too strict
|
|
100
106
|
ignore = [
|
|
101
107
|
"E501", # Line too long (handled by formatter)
|
|
102
108
|
"PLR0913", # Too many arguments
|
|
109
|
+
"PLR0912", # Too many branches
|
|
110
|
+
"PLR0914", # Too many locals
|
|
111
|
+
"PLR0915", # Too many statements
|
|
103
112
|
"PLR2004", # Magic value used in comparison
|
|
104
113
|
"PLC0415", # Import outside top-level (used to avoid circular imports)
|
|
114
|
+
"ANN401", # Allow `Any` in typed signatures
|
|
105
115
|
]
|
|
106
116
|
|
|
107
117
|
# Allow autofix for all enabled rules
|
|
@@ -110,7 +120,7 @@ unfixable = []
|
|
|
110
120
|
|
|
111
121
|
[tool.ruff.lint.per-file-ignores]
|
|
112
122
|
# Allow test files to have longer lines and more assertions
|
|
113
|
-
"tests/**/*.py" = ["E501", "PLR2004", "RUF002", "RUF003", "PLW0603"] #
|
|
123
|
+
"tests/**/*.py" = ["ANN", "E501", "PLR2004", "RUF002", "RUF003", "PLW0603", "ERA001"] # Tests don't need annotations (ANN); allow × in comments/docstrings; PLW0603 for spawn-pool worker globals; ERA001 for intentional commented-out fixtures
|
|
114
124
|
# Allow global variable usage in __init__.py (architectural decision)
|
|
115
125
|
"src/oda_data/__init__.py" = ["PLW0603", "PLW0602", "F811"]
|
|
116
126
|
# Lazy module-level state (override flag, chained-root cache, auto-migrate gate, 5GB warning latch).
|
|
@@ -126,6 +136,9 @@ unfixable = []
|
|
|
126
136
|
# Allow mixedCase variable names matching official sector code names
|
|
127
137
|
"src/oda_data/tools/sector_lists.py" = ["N816"]
|
|
128
138
|
|
|
139
|
+
[tool.ruff.lint.pydocstyle]
|
|
140
|
+
convention = "google"
|
|
141
|
+
|
|
129
142
|
[tool.ruff.format]
|
|
130
143
|
# Use double quotes for strings
|
|
131
144
|
quote-style = "double"
|
|
@@ -139,5 +152,86 @@ skip-magic-trailing-comma = false
|
|
|
139
152
|
# Like Black, automatically detect line endings
|
|
140
153
|
line-ending = "auto"
|
|
141
154
|
|
|
142
|
-
|
|
143
|
-
|
|
155
|
+
[tool.ty.environment]
|
|
156
|
+
python-version = "3.11"
|
|
157
|
+
|
|
158
|
+
[tool.ty.src]
|
|
159
|
+
include = ["src/oda_data"]
|
|
160
|
+
|
|
161
|
+
# NOTE: The following two sections are borrowed from the bblocks-projects
|
|
162
|
+
# `project` preset (the `package` preset omits them). This is a pandas/numpy-
|
|
163
|
+
# heavy package, so the same false-positive cascade from those libraries'
|
|
164
|
+
# incomplete stubs applies here. Kept as a deliberate divergence.
|
|
165
|
+
[tool.ty.analysis]
|
|
166
|
+
# Replace pandas / numpy type information with `Any` to eliminate the cascade
|
|
167
|
+
# of false positives from those libraries' incomplete or absent stubs without
|
|
168
|
+
# disabling type checking on our own code.
|
|
169
|
+
# https://docs.astral.sh/ty/reference/configuration/
|
|
170
|
+
replace-imports-with-any = [
|
|
171
|
+
"pandas",
|
|
172
|
+
"pandas.**",
|
|
173
|
+
"numpy",
|
|
174
|
+
"numpy.**",
|
|
175
|
+
]
|
|
176
|
+
|
|
177
|
+
[tool.ty.rules]
|
|
178
|
+
# These warn-by-default rules fire frequently on dynamic dataframe / dict
|
|
179
|
+
# patterns common in this codebase. Demoted to ignore.
|
|
180
|
+
possibly-missing-attribute = "ignore"
|
|
181
|
+
possibly-unresolved-reference = "ignore"
|
|
182
|
+
|
|
183
|
+
[tool.pytest.ini_options]
|
|
184
|
+
# Test discovery patterns
|
|
185
|
+
testpaths = ["tests"]
|
|
186
|
+
python_files = ["test_*.py"]
|
|
187
|
+
python_classes = ["Test*"]
|
|
188
|
+
python_functions = ["test_*"]
|
|
189
|
+
|
|
190
|
+
# Test execution options. Integration/network tests are deselected by default.
|
|
191
|
+
addopts = """
|
|
192
|
+
-v
|
|
193
|
+
--strict-markers
|
|
194
|
+
--tb=short
|
|
195
|
+
--cov=src/oda_data
|
|
196
|
+
--cov-report=term-missing
|
|
197
|
+
--cov-report=html
|
|
198
|
+
--cov-branch
|
|
199
|
+
-m "not integration and not network"
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
# Custom markers for organizing tests
|
|
203
|
+
markers = [
|
|
204
|
+
"slow: marks tests as slow (deselect with '-m \"not slow\"')",
|
|
205
|
+
"integration: marks tests that call real OECD API (deselected by default)",
|
|
206
|
+
"network: marks tests requiring live network access (opt-in via RUN_NETWORK_TESTS=1; deselected by default)",
|
|
207
|
+
"unit: marks tests as unit tests",
|
|
208
|
+
"cache: marks tests related to caching functionality",
|
|
209
|
+
"validation: marks tests related to input validation",
|
|
210
|
+
"cleaning: marks tests related to data cleaning",
|
|
211
|
+
]
|
|
212
|
+
|
|
213
|
+
# Directories not searched for tests
|
|
214
|
+
norecursedirs = [".git", ".venv", "venv", "__pycache__", "*.egg-info", ".pytest_cache", "htmlcov"]
|
|
215
|
+
|
|
216
|
+
[tool.coverage.run]
|
|
217
|
+
source = ["src/oda_data"]
|
|
218
|
+
branch = true
|
|
219
|
+
omit = [
|
|
220
|
+
"*/tests/*",
|
|
221
|
+
"*/__init__.py",
|
|
222
|
+
"*/config.py",
|
|
223
|
+
"*/logger.py",
|
|
224
|
+
]
|
|
225
|
+
|
|
226
|
+
[tool.coverage.report]
|
|
227
|
+
precision = 2
|
|
228
|
+
show_missing = true
|
|
229
|
+
exclude_lines = [
|
|
230
|
+
"pragma: no cover",
|
|
231
|
+
"def __repr__",
|
|
232
|
+
"raise AssertionError",
|
|
233
|
+
"raise NotImplementedError",
|
|
234
|
+
"if __name__ == .__main__.:",
|
|
235
|
+
"if TYPE_CHECKING:",
|
|
236
|
+
"@abstractmethod",
|
|
237
|
+
]
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import warnings
|
|
4
|
+
from pathlib import Path
|
|
2
5
|
|
|
3
6
|
from oda_data import cache
|
|
4
7
|
from oda_data.api.oecd import OECDClient
|
|
@@ -51,7 +54,7 @@ def enable_cache() -> None:
|
|
|
51
54
|
_enable("all")
|
|
52
55
|
|
|
53
56
|
|
|
54
|
-
def set_data_path(path) -> None:
|
|
57
|
+
def set_data_path(path: str | Path) -> None:
|
|
55
58
|
"""Set the path for data outputs (parquet exports).
|
|
56
59
|
|
|
57
60
|
Deprecated: cache location is now controlled via set_cache_root() or
|
|
@@ -60,8 +63,6 @@ def set_data_path(path) -> None:
|
|
|
60
63
|
"""
|
|
61
64
|
global _DEPRECATION_WARNED_SET_DATA_PATH
|
|
62
65
|
|
|
63
|
-
from pathlib import Path
|
|
64
|
-
|
|
65
66
|
from pydeflate import set_pydeflate_path
|
|
66
67
|
|
|
67
68
|
from oda_data.config import ODAPaths
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
1
2
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
2
3
|
from dataclasses import dataclass
|
|
3
4
|
from pathlib import Path
|
|
5
|
+
from typing import cast
|
|
4
6
|
|
|
5
7
|
import pandas as pd
|
|
6
8
|
|
|
@@ -29,7 +31,7 @@ source_to_module = {
|
|
|
29
31
|
"CRS": crs_functions,
|
|
30
32
|
}
|
|
31
33
|
|
|
32
|
-
READERS: dict[str,
|
|
34
|
+
READERS: dict[str, Callable] = {
|
|
33
35
|
"DAC1": DAC1Data,
|
|
34
36
|
"DAC2A": DAC2AData,
|
|
35
37
|
"CRS": CRSData,
|
|
@@ -63,7 +65,7 @@ def load_indicators() -> dict[str, dict]:
|
|
|
63
65
|
|
|
64
66
|
# Merge each inner dictionary into the combined dictionary
|
|
65
67
|
for indicators in (dac1_indicators, dac2a_indicators, crs_indicators):
|
|
66
|
-
for
|
|
68
|
+
for v in indicators.values():
|
|
67
69
|
combined |= v
|
|
68
70
|
|
|
69
71
|
return combined
|
|
@@ -122,9 +124,9 @@ class OECDClient:
|
|
|
122
124
|
continue
|
|
123
125
|
if self.use_bulk_download and source == "DAC2A":
|
|
124
126
|
continue
|
|
125
|
-
to_filter = [
|
|
126
|
-
|
|
127
|
-
|
|
127
|
+
to_filter = [
|
|
128
|
+
get_measure_filter(source, measure) for measure in self.measure
|
|
129
|
+
]
|
|
128
130
|
if to_filter is not None:
|
|
129
131
|
filters[source].append((col, "in", to_filter))
|
|
130
132
|
|
|
@@ -170,7 +172,7 @@ class OECDClient:
|
|
|
170
172
|
def _process_data(self, indicator: str) -> None:
|
|
171
173
|
"""Apply custom processing functions to the loaded data."""
|
|
172
174
|
custom_function = self._indicators[indicator].get("custom_function")
|
|
173
|
-
if
|
|
175
|
+
if custom_function is None or len(custom_function) < 1:
|
|
174
176
|
return
|
|
175
177
|
|
|
176
178
|
main_source = self._indicators[indicator]["sources"][0]
|
|
@@ -193,7 +195,7 @@ class OECDClient:
|
|
|
193
195
|
self.indicators_data[indicator]
|
|
194
196
|
)
|
|
195
197
|
|
|
196
|
-
def _group_data(self, indicator: str):
|
|
198
|
+
def _group_data(self, indicator: str) -> None:
|
|
197
199
|
"""Group the data to create semi-aggregated indicators (for valid sources)"""
|
|
198
200
|
main_source = self._indicators[indicator]["sources"][0]
|
|
199
201
|
|
|
@@ -203,7 +205,7 @@ class OECDClient:
|
|
|
203
205
|
self.indicators_data[indicator] = group_data_based_on_indicator(
|
|
204
206
|
data=self.indicators_data[indicator],
|
|
205
207
|
indicator_code=indicator,
|
|
206
|
-
measures=self.measure,
|
|
208
|
+
measures=cast("list[str] | Measure", self.measure),
|
|
207
209
|
)
|
|
208
210
|
|
|
209
211
|
def _convert_units(self, indicator: str) -> None:
|
|
@@ -217,7 +219,7 @@ class OECDClient:
|
|
|
217
219
|
)
|
|
218
220
|
|
|
219
221
|
@property
|
|
220
|
-
def arguments(self):
|
|
222
|
+
def arguments(self) -> dict:
|
|
221
223
|
"""Returns the arguments used by the object"""
|
|
222
224
|
return {
|
|
223
225
|
"years": self.years,
|
|
@@ -306,7 +308,7 @@ class OECDClient:
|
|
|
306
308
|
|
|
307
309
|
return pd.concat(
|
|
308
310
|
[
|
|
309
|
-
d.assign(one_indicator=i).dropna(axis=1, how="all")
|
|
311
|
+
cast(pd.DataFrame, d).assign(one_indicator=i).dropna(axis=1, how="all")
|
|
310
312
|
for i, d in self.indicators_data.items()
|
|
311
313
|
if i in indicators
|
|
312
314
|
],
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
class _ODAList(list):
|
|
2
|
-
def __repr__(self):
|
|
2
|
+
def __repr__(self) -> str:
|
|
3
3
|
# Return the elements with a line break between them
|
|
4
4
|
return "[\n" + ",\n".join(str(x) for x in self) + "\n]"
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class _ODADict(dict):
|
|
8
|
-
def __repr__(self):
|
|
8
|
+
def __repr__(self) -> str:
|
|
9
9
|
# Return the elements with a line break between them
|
|
10
10
|
return "{\n" + ",\n".join(f"{k}: {v}" for k, v in self.items()) + "\n}"
|