oda-data 2.4.0__tar.gz → 2.4.2__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.4.0 → oda_data-2.4.2}/PKG-INFO +1 -1
- {oda_data-2.4.0 → oda_data-2.4.2}/pyproject.toml +1 -1
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/api/sources.py +42 -10
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/clean_data/schema.py +2 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/tools/cache.py +19 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/README.md +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/__init__.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/api/__init__.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/api/constants.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/api/oecd.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/api/representations.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/clean_data/__init__.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/clean_data/channels.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/clean_data/common.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/clean_data/crs_channel_mapping.csv +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/clean_data/dtypes.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/clean_data/validation.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/config.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/indicators/__init__.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/indicators/common.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/indicators/crs/__init__.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/indicators/crs/common.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/indicators/crs/crs_functions.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/indicators/crs/crs_indicators.json +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/indicators/crs/oecd.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/indicators/crs/one.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/indicators/dac1/__init__.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/indicators/dac1/common.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/indicators/dac1/dac1_functions.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/indicators/dac1/dac1_indicators.json +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/indicators/dac1/oecd.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/indicators/dac1/one.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/indicators/dac2a/__init__.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/indicators/dac2a/common.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/indicators/dac2a/dac2a_functions.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/indicators/dac2a/dac2a_indicators.json +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/indicators/dac2a/oecd.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/indicators/dac2a/one.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/indicators/indicator.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/indicators/research/__init__.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/indicators/research/eu.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/indicators/research/policy_markers.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/indicators/research/sector_imputations.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/logger.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/oda_data_indicators.csv +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/settings/crs_flow_types.json +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/settings/crs_modalities.json +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/settings/crs_policy_markers.json +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/settings/crs_purpose.json +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/settings/crs_type_of_finance.json +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/settings/dac1_aid_flow_types.json +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/settings/dac2a_aid_type.json +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/settings/flow_types.json +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/settings/indicators.json +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/settings/key_columns.json +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/settings/provider_groupings.json +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/settings/recipient_groupings.json +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/settings/v1_indicators_mapping.json +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/tools/__init__.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/tools/compatibility.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/tools/gni.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/tools/groupings.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/tools/names/__init__.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/tools/names/add.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/tools/names/create_mapping.py +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/tools/names/crs_names.json +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/tools/names/dac1_names.json +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/tools/names/dac2a_names.json +0 -0
- {oda_data-2.4.0 → oda_data-2.4.2}/src/oda_data/tools/sector_lists.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: oda-data
|
|
3
|
-
Version: 2.4.
|
|
3
|
+
Version: 2.4.2
|
|
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>
|
|
@@ -37,6 +37,7 @@ from oda_data.tools.cache import (
|
|
|
37
37
|
create_crs_bulk_fetcher,
|
|
38
38
|
create_multisystem_bulk_fetcher,
|
|
39
39
|
generate_param_hash,
|
|
40
|
+
generate_projection_hash,
|
|
40
41
|
)
|
|
41
42
|
|
|
42
43
|
|
|
@@ -418,9 +419,20 @@ class DACSource(Source):
|
|
|
418
419
|
# 1. Try memory cache (thread-safe, fastest)
|
|
419
420
|
df = self.memory_cache.get(param_hash)
|
|
420
421
|
if df is not None:
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
422
|
+
# Only use if all requested columns are available
|
|
423
|
+
if not columns or all(c in df.columns for c in columns):
|
|
424
|
+
logger.info("Cache hit: memory")
|
|
425
|
+
return self._apply_columns_and_clean(
|
|
426
|
+
df, columns, already_cleaned=True
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
# For projected reads, also check a projection-specific memory cache
|
|
430
|
+
if columns:
|
|
431
|
+
proj_hash = generate_projection_hash(param_hash, columns)
|
|
432
|
+
df = self.memory_cache.get(proj_hash)
|
|
433
|
+
if df is not None:
|
|
434
|
+
logger.info("Cache hit: memory (projection)")
|
|
435
|
+
return df
|
|
424
436
|
|
|
425
437
|
# 2. Try query cache (on-disk parquet, fast)
|
|
426
438
|
df = self.query_cache.load(self.__class__.__name__, param_hash, None, columns)
|
|
@@ -435,9 +447,15 @@ class DACSource(Source):
|
|
|
435
447
|
|
|
436
448
|
# 3. Cache miss (query/memory) - need to fetch data
|
|
437
449
|
if using_bulk_download:
|
|
438
|
-
# Fetch from bulk cache with predicate pushdown and column selection
|
|
439
450
|
logger.info("Query cache miss - reading from bulk cache")
|
|
440
451
|
df = self._fetch_from_bulk_cache(filters, columns)
|
|
452
|
+
if columns:
|
|
453
|
+
# Cache projected result under a projection-specific key
|
|
454
|
+
# so it doesn't pollute the shared filter-only cache
|
|
455
|
+
self._cache_in_memory(
|
|
456
|
+
generate_projection_hash(param_hash, columns), df
|
|
457
|
+
)
|
|
458
|
+
return df
|
|
441
459
|
else:
|
|
442
460
|
# Download via API (uses init-time filters only)
|
|
443
461
|
logger.info("Cache miss - downloading via API")
|
|
@@ -801,9 +819,20 @@ class AidDataData(AidDataSource):
|
|
|
801
819
|
# 1. Try memory cache
|
|
802
820
|
df = self.memory_cache.get(param_hash)
|
|
803
821
|
if df is not None:
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
822
|
+
# Only use if all requested columns are available
|
|
823
|
+
if not columns or all(c in df.columns for c in columns):
|
|
824
|
+
logger.info("Cache hit: memory")
|
|
825
|
+
return self._apply_columns_and_clean(
|
|
826
|
+
df, columns, already_cleaned=True
|
|
827
|
+
)
|
|
828
|
+
|
|
829
|
+
# For projected reads, also check a projection-specific memory cache
|
|
830
|
+
if columns:
|
|
831
|
+
proj_hash = generate_projection_hash(param_hash, columns)
|
|
832
|
+
df = self.memory_cache.get(proj_hash)
|
|
833
|
+
if df is not None:
|
|
834
|
+
logger.info("Cache hit: memory (projection)")
|
|
835
|
+
return df
|
|
807
836
|
|
|
808
837
|
# 2. Try query cache
|
|
809
838
|
# Use column selection at parquet read level for efficiency
|
|
@@ -829,16 +858,19 @@ class AidDataData(AidDataSource):
|
|
|
829
858
|
# Use PyArrow predicate pushdown and column selection for maximum efficiency
|
|
830
859
|
pyarrow_filters = filters if filters else None
|
|
831
860
|
|
|
832
|
-
# If columns specified, read directly
|
|
861
|
+
# If columns specified, read directly with projection-specific caching
|
|
833
862
|
if columns:
|
|
834
863
|
logger.info(
|
|
835
864
|
"Query cache miss - reading from bulk cache with column selection"
|
|
836
865
|
)
|
|
837
|
-
# Targeted read: use full PyArrow optimization, no caching
|
|
838
866
|
df = pd.read_parquet(
|
|
839
867
|
bulk_path, filters=pyarrow_filters, columns=columns, engine="pyarrow"
|
|
840
868
|
)
|
|
841
|
-
#
|
|
869
|
+
# Cache under projection-specific key to avoid polluting
|
|
870
|
+
# the shared filter-only cache
|
|
871
|
+
self._cache_in_memory(
|
|
872
|
+
generate_projection_hash(param_hash, columns), df
|
|
873
|
+
)
|
|
842
874
|
return df
|
|
843
875
|
else:
|
|
844
876
|
logger.info("Query cache miss - reading from bulk cache")
|
|
@@ -435,6 +435,8 @@ CRS_MAPPING: dict[str, str] = CORE_CRS_MAPPING | {
|
|
|
435
435
|
"budget_ident": ODASchema.BUDGET_IDENTIFIER,
|
|
436
436
|
"usd_psi_gr_eq_memo": ODASchema.PSI_GRANT_EQ_USD,
|
|
437
437
|
"datatype_code": ODASchema.DATA_TYPE_CODE,
|
|
438
|
+
"donor": ODASchema.PROVIDER_NAME,
|
|
439
|
+
"recipient": ODASchema.RECIPIENT_NAME,
|
|
438
440
|
}
|
|
439
441
|
|
|
440
442
|
|
|
@@ -50,6 +50,25 @@ def generate_param_hash(filters: list[tuple]) -> str:
|
|
|
50
50
|
return hashlib.md5(json_str.encode("utf-8")).hexdigest()[:10]
|
|
51
51
|
|
|
52
52
|
|
|
53
|
+
def generate_projection_hash(param_hash: str, columns: list[str]) -> str:
|
|
54
|
+
"""Generates a cache key for a projected (column-subset) read.
|
|
55
|
+
|
|
56
|
+
Combines the filter-based param_hash with a hash of the requested columns,
|
|
57
|
+
so that projected reads are cached separately from full reads without
|
|
58
|
+
polluting the shared filter-only cache key.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
param_hash: The filter-only hash from generate_param_hash
|
|
62
|
+
columns: List of column names in the projection
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
A combined hash string
|
|
66
|
+
"""
|
|
67
|
+
col_str = json.dumps(sorted(columns), sort_keys=True)
|
|
68
|
+
col_hash = hashlib.md5(col_str.encode("utf-8")).hexdigest()[:10]
|
|
69
|
+
return f"{param_hash}_proj_{col_hash}"
|
|
70
|
+
|
|
71
|
+
|
|
53
72
|
class ThreadSafeMemoryCache:
|
|
54
73
|
"""Thread-safe wrapper around cachetools.TTLCache.
|
|
55
74
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|