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.
Files changed (74) hide show
  1. {oda_data-2.6.0 → oda_data-2.7.0}/PKG-INFO +28 -10
  2. {oda_data-2.6.0 → oda_data-2.7.0}/README.md +25 -7
  3. {oda_data-2.6.0 → oda_data-2.7.0}/pyproject.toml +100 -6
  4. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/__init__.py +4 -3
  5. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/api/oecd.py +12 -10
  6. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/api/representations.py +2 -2
  7. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/api/sources.py +79 -51
  8. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/cache/api.py +14 -10
  9. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/cache/types.py +6 -1
  10. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/clean_data/channels.py +8 -5
  11. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/clean_data/common.py +2 -2
  12. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/clean_data/validation.py +19 -8
  13. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/config.py +4 -2
  14. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/crs/common.py +6 -5
  15. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/crs/crs_functions.py +1 -1
  16. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/crs/oecd.py +25 -7
  17. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/crs/one.py +1 -1
  18. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/dac1/common.py +3 -3
  19. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/dac1/dac1_indicators.json +87 -51
  20. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/dac1/oecd.py +1 -1
  21. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/dac1/one.py +1 -1
  22. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/dac2a/oecd.py +1 -1
  23. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/dac2a/one.py +1 -1
  24. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/indicator.py +1 -1
  25. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/research/eu.py +10 -3
  26. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/research/policy_markers.py +2 -2
  27. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/research/sector_imputations.py +1 -1
  28. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/oda_data_indicators.csv +19 -17
  29. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/settings/flow_types.json +3 -1
  30. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/settings/provider_groupings.json +2 -2
  31. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/settings/recipient_groupings.json +1 -1
  32. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/tools/cache.py +12 -12
  33. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/tools/compatibility.py +7 -5
  34. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/tools/gni.py +2 -0
  35. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/tools/names/create_mapping.py +4 -4
  36. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/tools/names/crs_names.json +4 -5
  37. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/tools/names/dac1_names.json +1 -1
  38. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/tools/names/dac2a_names.json +3 -3
  39. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/tools/sector_lists.py +2 -2
  40. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/api/__init__.py +0 -0
  41. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/api/constants.py +0 -0
  42. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/cache/__init__.py +0 -0
  43. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/cache/_migrate.py +0 -0
  44. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/cache/config.py +0 -0
  45. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/clean_data/__init__.py +0 -0
  46. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/clean_data/crs_channel_mapping.csv +0 -0
  47. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/clean_data/dtypes.py +0 -0
  48. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/clean_data/schema.py +0 -0
  49. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/__init__.py +0 -0
  50. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/common.py +0 -0
  51. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/crs/__init__.py +0 -0
  52. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/crs/crs_indicators.json +0 -0
  53. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/dac1/__init__.py +0 -0
  54. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/dac1/dac1_functions.py +0 -0
  55. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/dac2a/__init__.py +0 -0
  56. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/dac2a/common.py +0 -0
  57. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/dac2a/dac2a_functions.py +0 -0
  58. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/dac2a/dac2a_indicators.json +0 -0
  59. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/indicators/research/__init__.py +0 -0
  60. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/logger.py +0 -0
  61. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/settings/crs_flow_types.json +0 -0
  62. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/settings/crs_modalities.json +0 -0
  63. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/settings/crs_policy_markers.json +0 -0
  64. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/settings/crs_purpose.json +0 -0
  65. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/settings/crs_type_of_finance.json +0 -0
  66. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/settings/dac1_aid_flow_types.json +0 -0
  67. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/settings/dac2a_aid_type.json +0 -0
  68. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/settings/indicators.json +0 -0
  69. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/settings/key_columns.json +0 -0
  70. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/settings/v1_indicators_mapping.json +0 -0
  71. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/tools/__init__.py +0 -0
  72. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/tools/groupings.py +0 -0
  73. {oda_data-2.6.0 → oda_data-2.7.0}/src/oda_data/tools/names/__init__.py +0 -0
  74. {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: oda-data
3
- Version: 2.6.0
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
  [![pypi](https://img.shields.io/pypi/v/oda_data.svg)](https://pypi.org/project/oda_data/)
@@ -39,6 +39,7 @@ Description-Content-Type: text/markdown
39
39
  [![Downloads](https://static.pepy.tech/badge/oda-data)](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
  [![Downloads](https://static.pepy.tech/badge/oda-data)](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.6.0"
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.22,<0.9.0"]
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"] # Allow × in comments/docstrings; PLW0603 for spawn-pool worker globals
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
- # Note: pytest configuration lives in `pytest.ini` (which takes precedence over
143
- # this file). Markers and addopts must be added there, not here.
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, callable] = {
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 _k, v in indicators.items():
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
- for measure in self.measure:
127
- to_filter.append(get_measure_filter(source, measure))
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 (len(custom_function) < 1) or custom_function is None:
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}"