hestia-earth-models 0.73.6__py3-none-any.whl → 0.73.8__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.
Potentially problematic release.
This version of hestia-earth-models might be problematic. Click here for more details.
- hestia_earth/models/config/Cycle.json +116 -26
- hestia_earth/models/config/ImpactAssessment.json +239 -199
- hestia_earth/models/dammgen2009/noxToAirExcreta.py +11 -9
- hestia_earth/models/ecoalimV9/cycle.py +29 -39
- hestia_earth/models/ecoalimV9/impact_assessment.py +38 -40
- hestia_earth/models/ecoalimV9/utils.py +82 -16
- hestia_earth/models/ecoinventV3/__init__.py +3 -3
- hestia_earth/models/emepEea2019/n2OToAirFuelCombustionDirect.py +2 -2
- hestia_earth/models/hestia/default_emissions.py +2 -6
- hestia_earth/models/hestia/default_resourceUse.py +2 -5
- hestia_earth/models/hestia/landCover.py +3 -3
- hestia_earth/models/hestia/pastureSystem.py +1 -1
- hestia_earth/models/hestia/seed_emissions.py +7 -3
- hestia_earth/models/impact_assessment/emissions.py +3 -5
- hestia_earth/models/ipcc2019/biocharOrganicCarbonPerHa.py +9 -3
- hestia_earth/models/ipcc2019/co2ToAirAboveGroundBiomassStockChange.py +1 -5
- hestia_earth/models/ipcc2019/co2ToAirBelowGroundBiomassStockChange.py +1 -5
- hestia_earth/models/ipcc2019/co2ToAirCarbonStockChange_utils.py +1 -33
- hestia_earth/models/ipcc2019/co2ToAirSoilOrganicCarbonStockChange.py +1 -5
- hestia_earth/models/ipcc2019/n2OToAirAquacultureSystemsIndirect.py +44 -0
- hestia_earth/models/ipcc2019/n2OToAirCropResidueBurningIndirect.py +43 -0
- hestia_earth/models/ipcc2019/n2OToAirCropResidueDecompositionIndirect.py +13 -70
- hestia_earth/models/ipcc2019/n2OToAirExcretaIndirect.py +13 -70
- hestia_earth/models/ipcc2019/n2OToAirFuelCombustionIndirect.py +43 -0
- hestia_earth/models/ipcc2019/n2OToAirInorganicFertiliserIndirect.py +13 -70
- hestia_earth/models/ipcc2019/n2OToAirNaturalVegetationBurningIndirect.py +43 -0
- hestia_earth/models/ipcc2019/n2OToAirOrganicFertiliserIndirect.py +13 -70
- hestia_earth/models/ipcc2019/n2OToAirOrganicSoilBurningIndirect.py +43 -0
- hestia_earth/models/ipcc2019/n2OToAirOrganicSoilCultivationIndirect.py +43 -0
- hestia_earth/models/ipcc2019/n2OToAir_indirect_emissions_utils.py +112 -0
- hestia_earth/models/ipcc2019/utils.py +0 -25
- hestia_earth/models/jarvisAndPain1994/n2ToAirExcreta.py +11 -9
- hestia_earth/models/linkedImpactAssessment/emissions.py +24 -15
- hestia_earth/models/linkedImpactAssessment/utils.py +5 -1
- hestia_earth/models/mocking/search-results.json +1284 -1284
- hestia_earth/models/utils/background_emissions.py +17 -10
- hestia_earth/models/utils/emission.py +18 -8
- hestia_earth/models/utils/impact_assessment.py +3 -3
- hestia_earth/models/utils/indicator.py +8 -1
- hestia_earth/models/utils/lookup.py +38 -21
- hestia_earth/models/utils/productivity.py +1 -1
- hestia_earth/models/version.py +1 -1
- hestia_earth/orchestrator/strategies/merge/merge_list.py +41 -54
- {hestia_earth_models-0.73.6.dist-info → hestia_earth_models-0.73.8.dist-info}/METADATA +3 -3
- {hestia_earth_models-0.73.6.dist-info → hestia_earth_models-0.73.8.dist-info}/RECORD +64 -49
- tests/models/dammgen2009/test_noxToAirExcreta.py +2 -2
- tests/models/ecoalimV9/test_cycle.py +1 -1
- tests/models/ecoalimV9/test_impact_assessment.py +1 -1
- tests/models/ecoalimV9/test_utils.py +13 -0
- tests/models/ipcc2019/test_biocharOrganicCarbonPerHa.py +2 -1
- tests/models/ipcc2019/test_n2OToAirAquacultureSystemsIndirect.py +45 -0
- tests/models/ipcc2019/test_n2OToAirCropResidueBurningIndirect.py +45 -0
- tests/models/ipcc2019/test_n2OToAirCropResidueDecompositionIndirect.py +6 -32
- tests/models/ipcc2019/test_n2OToAirExcretaIndirect.py +6 -32
- tests/models/ipcc2019/test_n2OToAirFuelCombustionIndirect.py +45 -0
- tests/models/ipcc2019/test_n2OToAirInorganicFertiliserIndirect.py +6 -32
- tests/models/ipcc2019/test_n2OToAirNaturalVegetationBurningIndirect.py +45 -0
- tests/models/ipcc2019/test_n2OToAirOrganicFertiliserIndirect.py +6 -32
- tests/models/ipcc2019/test_n2OToAirOrganicSoilBurningIndirect.py +45 -0
- tests/models/ipcc2019/test_n2OToAirOrganicSoilCultivationIndirect.py +45 -0
- tests/models/ipcc2019/test_n2OToAir_indirect_emissions_utils.py +19 -0
- {hestia_earth_models-0.73.6.dist-info → hestia_earth_models-0.73.8.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.73.6.dist-info → hestia_earth_models-0.73.8.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.73.6.dist-info → hestia_earth_models-0.73.8.dist-info}/top_level.txt +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
from hestia_earth.schema import TermTermType
|
|
2
2
|
from hestia_earth.utils.model import find_term_match, filter_list_term_type
|
|
3
|
-
from hestia_earth.utils.tools import flatten
|
|
3
|
+
from hestia_earth.utils.tools import flatten, non_empty_list
|
|
4
4
|
from hestia_earth.utils.emission import cycle_emissions_in_system_boundary
|
|
5
5
|
|
|
6
|
-
from hestia_earth.models.log import logShouldRun
|
|
6
|
+
from hestia_earth.models.log import logShouldRun, debugValues
|
|
7
7
|
from . import is_from_model
|
|
8
8
|
from .term import get_lookup_value
|
|
9
9
|
|
|
@@ -58,23 +58,30 @@ def no_gap_filled_background_emissions(
|
|
|
58
58
|
return check_input
|
|
59
59
|
|
|
60
60
|
|
|
61
|
-
def all_background_emission_term_ids(
|
|
62
|
-
term_ids = cycle_emissions_in_system_boundary(
|
|
61
|
+
def all_background_emission_term_ids(node: dict, termType: TermTermType):
|
|
62
|
+
term_ids = cycle_emissions_in_system_boundary(node, termType=termType)
|
|
63
63
|
return list(set([
|
|
64
|
-
get_lookup_value({'termType':
|
|
64
|
+
get_lookup_value({'termType': termType.value, '@id': term_id}, 'inputProductionGroupId')
|
|
65
65
|
for term_id in term_ids
|
|
66
66
|
]))
|
|
67
67
|
|
|
68
68
|
|
|
69
|
-
def log_missing_emissions(
|
|
70
|
-
all_emission_term_ids = all_background_emission_term_ids(
|
|
69
|
+
def log_missing_emissions(node: dict, termType: TermTermType = TermTermType.EMISSION, **log_args):
|
|
70
|
+
all_emission_term_ids = all_background_emission_term_ids(node, termType)
|
|
71
71
|
|
|
72
72
|
def log_input(input_term_id: str, included_emission_term_ids: list, **extra_log_args):
|
|
73
|
-
missing_emission_term_ids = [
|
|
73
|
+
missing_emission_term_ids = non_empty_list([
|
|
74
74
|
term_id for term_id in all_emission_term_ids if term_id not in included_emission_term_ids
|
|
75
|
-
]
|
|
75
|
+
])
|
|
76
76
|
for emission_id in missing_emission_term_ids:
|
|
77
|
-
|
|
77
|
+
# debug value on the emission itself so it appears for the input
|
|
78
|
+
debugValues(node, term=emission_id,
|
|
79
|
+
value=None,
|
|
80
|
+
coefficient=None,
|
|
81
|
+
input=input_term_id,
|
|
82
|
+
**log_args,
|
|
83
|
+
**extra_log_args)
|
|
84
|
+
logShouldRun(node, term=input_term_id, should_run=False, emission_id=emission_id,
|
|
78
85
|
**log_args,
|
|
79
86
|
**extra_log_args)
|
|
80
87
|
return log_input
|
|
@@ -2,7 +2,7 @@ from collections.abc import Iterable
|
|
|
2
2
|
from typing import Optional, Union
|
|
3
3
|
from hestia_earth.schema import EmissionMethodTier, SchemaType, TermTermType
|
|
4
4
|
from hestia_earth.utils.model import linked_node
|
|
5
|
-
|
|
5
|
+
from hestia_earth.utils.emission import cycle_emissions_in_system_boundary, emissions_in_system_boundary
|
|
6
6
|
|
|
7
7
|
from . import flatten_args
|
|
8
8
|
from .term import download_term
|
|
@@ -13,20 +13,22 @@ from .constant import Units, get_atomic_conversion
|
|
|
13
13
|
EMISSION_METHOD_TIERS = [e.value for e in EmissionMethodTier]
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
def _new_emission(term, model=None):
|
|
16
|
+
def _new_emission(term, model=None, country_id: str = None, key_id: str = None):
|
|
17
17
|
node = {'@type': SchemaType.EMISSION.value}
|
|
18
18
|
node['term'] = linked_node(term if isinstance(term, dict) else download_term(term, TermTermType.EMISSION))
|
|
19
|
+
if country_id:
|
|
20
|
+
node['country'] = linked_node(download_term(country_id, TermTermType.REGION))
|
|
21
|
+
if key_id:
|
|
22
|
+
node['key'] = linked_node(download_term(key_id))
|
|
19
23
|
return include_methodModel(node, model)
|
|
20
24
|
|
|
21
25
|
|
|
22
|
-
def get_nh3_no3_nox_to_n(cycle: dict, nh3_term_id: str, no3_term_id: str, nox_term_id: str
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
nh3 = find_terms_value(cycle.get('emissions', []), nh3_term_id, default=default_value)
|
|
26
|
+
def get_nh3_no3_nox_to_n(cycle: dict, nh3_term_id: str = None, no3_term_id: str = None, nox_term_id: str = None):
|
|
27
|
+
nh3 = find_terms_value(cycle.get('emissions', []), nh3_term_id, default=None)
|
|
26
28
|
nh3 = None if nh3 is None else nh3 / get_atomic_conversion(Units.KG_NH3, Units.TO_N)
|
|
27
|
-
no3 = find_terms_value(cycle.get('emissions', []), no3_term_id, default=
|
|
29
|
+
no3 = find_terms_value(cycle.get('emissions', []), no3_term_id, default=None)
|
|
28
30
|
no3 = None if no3 is None else no3 / get_atomic_conversion(Units.KG_NO3, Units.TO_N)
|
|
29
|
-
nox = find_terms_value(cycle.get('emissions', []), nox_term_id, default=
|
|
31
|
+
nox = find_terms_value(cycle.get('emissions', []), nox_term_id, default=None)
|
|
30
32
|
nox = None if nox is None else nox / get_atomic_conversion(Units.KG_NOX, Units.TO_N)
|
|
31
33
|
|
|
32
34
|
return (nh3, no3, nox)
|
|
@@ -100,3 +102,11 @@ def to_emission_method_tier(method: Union[EmissionMethodTier, str]) -> Optional[
|
|
|
100
102
|
def filter_emission_inputs(emission: dict, term_type: TermTermType):
|
|
101
103
|
inputs = emission.get('inputs', [])
|
|
102
104
|
return [i for i in inputs if i.get('termType') == term_type.value]
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def background_emissions_in_system_boundary(node: dict, term_type: TermTermType = TermTermType.EMISSION):
|
|
108
|
+
term_ids = (
|
|
109
|
+
cycle_emissions_in_system_boundary(node, term_type) if term_type == TermTermType.EMISSION else
|
|
110
|
+
emissions_in_system_boundary(term_type)
|
|
111
|
+
)
|
|
112
|
+
return [id for id in term_ids if 'InputsProduction' in id]
|
|
@@ -4,7 +4,7 @@ from hestia_earth.utils.model import filter_list_term_type
|
|
|
4
4
|
from hestia_earth.utils.tools import list_sum, safe_parse_date
|
|
5
5
|
|
|
6
6
|
from hestia_earth.models.log import debugValues
|
|
7
|
-
from .lookup import all_factor_value,
|
|
7
|
+
from .lookup import all_factor_value, region_factor_value, aware_factor_value, fallback_country
|
|
8
8
|
from .product import find_by_product
|
|
9
9
|
from .site import region_level_1_id
|
|
10
10
|
|
|
@@ -190,7 +190,7 @@ def impact_country_value(
|
|
|
190
190
|
blank_nodes=blank_nodes,
|
|
191
191
|
grouped_key=group_key,
|
|
192
192
|
default_no_values=default_no_values,
|
|
193
|
-
factor_value_func=
|
|
193
|
+
factor_value_func=region_factor_value
|
|
194
194
|
)
|
|
195
195
|
|
|
196
196
|
|
|
@@ -231,7 +231,7 @@ def impact_aware_value(model: str, term_id: str, impact: dict, lookup: str, grou
|
|
|
231
231
|
blank_nodes=blank_nodes,
|
|
232
232
|
grouped_key=group_key,
|
|
233
233
|
default_no_values=None,
|
|
234
|
-
factor_value_func=
|
|
234
|
+
factor_value_func=aware_factor_value
|
|
235
235
|
)
|
|
236
236
|
|
|
237
237
|
|
|
@@ -5,7 +5,10 @@ from .method import include_methodModel
|
|
|
5
5
|
from .term import download_term
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
def _new_indicator(
|
|
8
|
+
def _new_indicator(
|
|
9
|
+
term: dict, model=None,
|
|
10
|
+
land_cover_id: str = None, previous_land_cover_id: str = None, country_id: str = None, key_id: str = None
|
|
11
|
+
):
|
|
9
12
|
node = {'@type': SchemaType.INDICATOR.value}
|
|
10
13
|
node['term'] = linked_node(term if isinstance(term, dict) else download_term(
|
|
11
14
|
term, TermTermType.CHARACTERISEDINDICATOR)
|
|
@@ -14,4 +17,8 @@ def _new_indicator(term, model=None, land_cover_id: str = None, previous_land_co
|
|
|
14
17
|
node['landCover'] = linked_node(download_term(land_cover_id, TermTermType.LANDCOVER))
|
|
15
18
|
if previous_land_cover_id:
|
|
16
19
|
node['previousLandCover'] = linked_node(download_term(previous_land_cover_id, TermTermType.LANDCOVER))
|
|
20
|
+
if country_id:
|
|
21
|
+
node['country'] = linked_node(download_term(country_id, TermTermType.REGION))
|
|
22
|
+
if key_id:
|
|
23
|
+
node['key'] = linked_node(download_term(key_id))
|
|
17
24
|
return include_methodModel(node, model)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
from functools import lru_cache
|
|
2
2
|
from typing import Optional, List
|
|
3
3
|
from hestia_earth.utils.lookup import (
|
|
4
4
|
download_lookup,
|
|
@@ -19,16 +19,20 @@ def _node_value(node):
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
def _factor_value(model: str, term_id: str, lookup_name: str, lookup_col: str, grouped_key: Optional[str] = None):
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
grouped_data_key = grouped_key or data.get('methodModel', {}).get('@id')
|
|
25
|
-
value = _node_value(data)
|
|
22
|
+
@lru_cache()
|
|
23
|
+
def get_coefficient(node_term_id: str, grouped_data_key: str):
|
|
26
24
|
coefficient = get_region_lookup_value(lookup_name, node_term_id, lookup_col, model=model, term=term_id)
|
|
27
25
|
# value is either a number or matching between a model and a value (restrict value to specific model only)
|
|
28
|
-
|
|
26
|
+
return safe_parse_float(
|
|
29
27
|
extract_grouped_data(coefficient, grouped_data_key),
|
|
30
28
|
default=None
|
|
31
29
|
) if ':' in str(coefficient) else safe_parse_float(coefficient, default=None)
|
|
30
|
+
|
|
31
|
+
def get_value(data: dict):
|
|
32
|
+
node_term_id = data.get('term', {}).get('@id')
|
|
33
|
+
grouped_data_key = grouped_key or data.get('methodModel', {}).get('@id')
|
|
34
|
+
value = _node_value(data)
|
|
35
|
+
coefficient = get_coefficient(node_term_id, grouped_data_key)
|
|
32
36
|
if value is not None and coefficient is not None:
|
|
33
37
|
if model:
|
|
34
38
|
debugValues(data, model=model, term=term_id,
|
|
@@ -40,7 +44,15 @@ def _factor_value(model: str, term_id: str, lookup_name: str, lookup_col: str, g
|
|
|
40
44
|
return get_value
|
|
41
45
|
|
|
42
46
|
|
|
43
|
-
def
|
|
47
|
+
def region_factor_value(model: str, term_id: str, lookup_name: str, lookup_term_id: str, group_key: str = None):
|
|
48
|
+
@lru_cache()
|
|
49
|
+
def get_coefficient(node_term_id: str, region_term_id: str):
|
|
50
|
+
coefficient = get_region_lookup_value(lookup_name, region_term_id, node_term_id, model=model, term=term_id)
|
|
51
|
+
return safe_parse_float(
|
|
52
|
+
extract_grouped_data(coefficient, group_key) if group_key else coefficient,
|
|
53
|
+
default=None
|
|
54
|
+
)
|
|
55
|
+
|
|
44
56
|
def get_value(data: dict):
|
|
45
57
|
node_term_id = data.get('term', {}).get('@id')
|
|
46
58
|
value = _node_value(data)
|
|
@@ -48,11 +60,7 @@ def _region_factor_value(model: str, term_id: str, lookup_name: str, lookup_term
|
|
|
48
60
|
region_term_id = (
|
|
49
61
|
(data.get('region') or data.get('country') or {'@id': lookup_term_id}).get('@id')
|
|
50
62
|
) if lookup_term_id.startswith('GADM-') else lookup_term_id
|
|
51
|
-
coefficient =
|
|
52
|
-
coefficient = safe_parse_float(
|
|
53
|
-
extract_grouped_data(coefficient, group_key) if group_key else coefficient,
|
|
54
|
-
default=None
|
|
55
|
-
)
|
|
63
|
+
coefficient = get_coefficient(node_term_id, region_term_id)
|
|
56
64
|
if value is not None and coefficient is not None:
|
|
57
65
|
debugValues(data, model=model, term=term_id,
|
|
58
66
|
node=node_term_id,
|
|
@@ -62,20 +70,24 @@ def _region_factor_value(model: str, term_id: str, lookup_name: str, lookup_term
|
|
|
62
70
|
return get_value
|
|
63
71
|
|
|
64
72
|
|
|
65
|
-
def
|
|
73
|
+
def aware_factor_value(model: str, term_id: str, lookup_name: str, aware_id: str, group_key: str = None):
|
|
66
74
|
lookup = download_lookup(lookup_name, False) # avoid saving in memory as there could be many different files used
|
|
67
75
|
lookup_col = column_name('awareWaterBasinId')
|
|
68
76
|
|
|
77
|
+
@lru_cache()
|
|
78
|
+
def get_coefficient(node_term_id: str):
|
|
79
|
+
coefficient = _get_single_table_value(lookup, lookup_col, int(aware_id), column_name(node_term_id))
|
|
80
|
+
return safe_parse_float(
|
|
81
|
+
extract_grouped_data(coefficient, group_key),
|
|
82
|
+
default=None
|
|
83
|
+
) if group_key else coefficient
|
|
84
|
+
|
|
69
85
|
def get_value(data: dict):
|
|
70
86
|
node_term_id = data.get('term', {}).get('@id')
|
|
71
87
|
value = _node_value(data)
|
|
72
88
|
|
|
73
89
|
try:
|
|
74
|
-
coefficient =
|
|
75
|
-
coefficient = safe_parse_float(
|
|
76
|
-
extract_grouped_data(coefficient, group_key),
|
|
77
|
-
default=None
|
|
78
|
-
) if group_key else coefficient
|
|
90
|
+
coefficient = get_coefficient(node_term_id)
|
|
79
91
|
if value is not None and coefficient is not None:
|
|
80
92
|
debugValues(data, model=model, term=term_id,
|
|
81
93
|
node=node_term_id,
|
|
@@ -159,13 +171,18 @@ def fallback_country(country_id: str, lookups: List[str]) -> str:
|
|
|
159
171
|
return country_id if country_id and is_in_lookup(country_id) else fallback_id if is_in_lookup(fallback_id) else None
|
|
160
172
|
|
|
161
173
|
|
|
162
|
-
|
|
163
|
-
def get_region_lookup_value(lookup_name: str, term_id: str, column: str, **log_args):
|
|
174
|
+
def get_region_lookup(lookup_name: str, term_id: str):
|
|
164
175
|
# for performance, try to load the region specific lookup if exists
|
|
165
|
-
|
|
176
|
+
return (
|
|
166
177
|
download_lookup(lookup_name.replace('region-', f"{term_id}-"))
|
|
167
178
|
if lookup_name and lookup_name.startswith('region-') else None
|
|
168
179
|
) or download_lookup(lookup_name)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
@lru_cache()
|
|
183
|
+
def get_region_lookup_value(lookup_name: str, term_id: str, column: str, **log_args):
|
|
184
|
+
# for performance, try to load the region specific lookup if exists
|
|
185
|
+
lookup = get_region_lookup(lookup_name, term_id)
|
|
169
186
|
value = get_table_value(lookup, 'termid', term_id, column_name(column))
|
|
170
187
|
debugMissingLookup(lookup_name, 'termid', term_id, column, value, **log_args)
|
|
171
188
|
return value
|
|
@@ -17,5 +17,5 @@ PRODUCTIVITY_KEY = {
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
def get_productivity(country: dict, default: PRODUCTIVITY = PRODUCTIVITY.HIGH):
|
|
20
|
-
hdi = safe_parse_float(get_lookup_value(country, '
|
|
20
|
+
hdi = safe_parse_float(get_lookup_value(country, 'HDI'), default=None)
|
|
21
21
|
return next((key for key in PRODUCTIVITY_KEY if hdi and PRODUCTIVITY_KEY[key](hdi)), default)
|
hestia_earth/models/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
VERSION = '0.73.
|
|
1
|
+
VERSION = '0.73.8'
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import pydash
|
|
2
2
|
from datetime import datetime
|
|
3
3
|
from hestia_earth.schema import UNIQUENESS_FIELDS
|
|
4
|
-
from hestia_earth.utils.tools import safe_parse_date
|
|
4
|
+
from hestia_earth.utils.tools import safe_parse_date, flatten
|
|
5
5
|
|
|
6
6
|
from hestia_earth.orchestrator.utils import _non_empty_list, update_node_version
|
|
7
7
|
from .merge_node import merge as merge_node
|
|
@@ -27,39 +27,6 @@ def _has_property(value: dict, key: str):
|
|
|
27
27
|
def _values_have_property(values: list, key: str): return any([_has_property(v, key) for v in values])
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
def _match_list_el(source: list, dest: list, key: str):
|
|
31
|
-
src_value = sorted(_non_empty_list([pydash.objects.get(x, key) for x in source]))
|
|
32
|
-
dest_value = sorted(_non_empty_list([pydash.objects.get(x, key) for x in dest]))
|
|
33
|
-
return src_value == dest_value
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def _get_value(data: dict, key: str, merge_args: dict = {}):
|
|
37
|
-
value = pydash.objects.get(data, key)
|
|
38
|
-
date = safe_parse_date(value) if key in ['startDate', 'endDate'] else None
|
|
39
|
-
return datetime.strftime(date, merge_args.get('matchDatesFormat', '%Y-%m-%d')) if date else value
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def _match_el(source: dict, dest: dict, keys: list, merge_args: dict = {}):
|
|
43
|
-
def match(key: str):
|
|
44
|
-
keys = key.split('.')
|
|
45
|
-
src_value = _get_value(source, key, merge_args)
|
|
46
|
-
dest_value = _get_value(dest, key, merge_args)
|
|
47
|
-
is_list = len(keys) >= 2 and (
|
|
48
|
-
isinstance(pydash.objects.get(source, keys[0]), list) or
|
|
49
|
-
isinstance(pydash.objects.get(dest, keys[0]), list)
|
|
50
|
-
)
|
|
51
|
-
return _match_list_el(
|
|
52
|
-
pydash.objects.get(source, keys[0], []),
|
|
53
|
-
pydash.objects.get(dest, keys[0], []),
|
|
54
|
-
'.'.join(keys[1:])
|
|
55
|
-
) if is_list else src_value == dest_value
|
|
56
|
-
|
|
57
|
-
source_properties = [p for p in keys if _has_property(source, p)]
|
|
58
|
-
dest_properties = [p for p in keys if _has_property(dest, p)]
|
|
59
|
-
|
|
60
|
-
return all(map(match, source_properties)) if source_properties == dest_properties else False
|
|
61
|
-
|
|
62
|
-
|
|
63
30
|
def _handle_local_property(values: list, properties: list, local_id: str):
|
|
64
31
|
# Handle "impactAssessment.@id" if present in the data
|
|
65
32
|
existing_id = local_id.replace('.id', '.@id')
|
|
@@ -76,38 +43,58 @@ def _handle_local_property(values: list, properties: list, local_id: str):
|
|
|
76
43
|
return properties
|
|
77
44
|
|
|
78
45
|
|
|
79
|
-
def
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
46
|
+
def _get_value(data: dict, key: str, merge_args: dict = {}):
|
|
47
|
+
value = pydash.objects.get(data, key)
|
|
48
|
+
date = safe_parse_date(value) if key in ['startDate', 'endDate'] else None
|
|
49
|
+
return datetime.strftime(date, merge_args.get('matchDatesFormat', '%Y-%m-%d')) if date else value
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _value_index_key(value: dict, properties: list, merge_args: dict = {}):
|
|
53
|
+
def property_value(key: str):
|
|
54
|
+
keys = key.split('.')
|
|
55
|
+
prop_value = _get_value(value, key, merge_args)
|
|
56
|
+
is_list = len(keys) >= 2 and isinstance(pydash.objects.get(value, keys[0]), list)
|
|
57
|
+
return sorted(_non_empty_list([
|
|
58
|
+
pydash.objects.get(x, '.'.join(keys[1:]))
|
|
59
|
+
for x in pydash.objects.get(value, keys[0], [])
|
|
60
|
+
])) if is_list else prop_value
|
|
61
|
+
|
|
62
|
+
source_properties = [p for p in properties if _has_property(value, p)]
|
|
63
|
+
return '-'.join(map(str, flatten(map(property_value, source_properties))))
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _build_matching_properties(values: list, model: dict = {}, merge_args: dict = {}, node_type: str = ''):
|
|
67
|
+
# only merge node if it has the same `methodModel`
|
|
68
|
+
same_methodModel = merge_args.get('sameMethodModel', False)
|
|
83
69
|
|
|
84
|
-
1. Update list of properties to handle `methodModel.@id` and `impactAssessment.@id`
|
|
85
|
-
2. Filter values that have the same unique properties as el
|
|
86
|
-
3. Make sure all shared unique properties are identical
|
|
87
|
-
"""
|
|
88
70
|
properties = _matching_properties(model, node_type)
|
|
89
71
|
properties = list(set(properties + [_METHOD_MODEL_KEY])) if same_methodModel else [
|
|
90
72
|
p for p in properties if p != _METHOD_MODEL_KEY
|
|
91
73
|
]
|
|
92
|
-
|
|
74
|
+
return _handle_local_property(values, properties, 'impactAssessment.id')
|
|
93
75
|
|
|
94
|
-
return next(
|
|
95
|
-
(i for i in range(len(values)) if _match_el(values[i], el, properties, merge_args)),
|
|
96
|
-
None
|
|
97
|
-
) if properties else None
|
|
98
76
|
|
|
77
|
+
def merge(source: list, new_values: list, version: str, model: dict = {}, merge_args: dict = {}, node_type: str = ''):
|
|
78
|
+
source = [] if source is None else source
|
|
99
79
|
|
|
100
|
-
def merge(source: list, merge_with: list, version: str, model: dict = {}, merge_args: dict = {}, node_type: str = ''):
|
|
101
|
-
source = source if source is not None else []
|
|
102
|
-
|
|
103
|
-
# only merge node if it has the same `methodModel`
|
|
104
|
-
same_methodModel = merge_args.get('sameMethodModel', False)
|
|
105
80
|
# only merge if the
|
|
106
81
|
skip_same_term = merge_args.get('skipSameTerm', False)
|
|
107
82
|
|
|
108
|
-
|
|
109
|
-
|
|
83
|
+
# build list of properties used to do the matching
|
|
84
|
+
properties = _build_matching_properties(source, model, merge_args, node_type)
|
|
85
|
+
|
|
86
|
+
source_index_keys = {
|
|
87
|
+
_value_index_key(value, properties, merge_args): index
|
|
88
|
+
for index, value in enumerate(source)
|
|
89
|
+
} if properties else None
|
|
90
|
+
|
|
91
|
+
for el in _non_empty_list(new_values):
|
|
92
|
+
new_value_index_key = _value_index_key(el, properties, merge_args)
|
|
93
|
+
source_index = source_index_keys.get(new_value_index_key) if source_index_keys else None
|
|
110
94
|
if source_index is None:
|
|
95
|
+
# add to index keys for next elements
|
|
96
|
+
if source_index_keys:
|
|
97
|
+
source_index_keys[new_value_index_key] = len(source)
|
|
111
98
|
source.append(update_node_version(version, el))
|
|
112
99
|
elif not skip_same_term:
|
|
113
100
|
source[source_index] = merge_node(source[source_index], el, version, model, merge_args)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: hestia-earth-models
|
|
3
|
-
Version: 0.73.
|
|
3
|
+
Version: 0.73.8
|
|
4
4
|
Summary: HESTIA's set of modules for filling gaps in the activity data using external datasets (e.g. populating soil properties with a geospatial dataset using provided coordinates) and internal lookups (e.g. populating machinery use from fuel use). Includes rules for when gaps should be filled versus not (e.g. never gap fill yield, gap fill crop residue if yield provided etc.).
|
|
5
5
|
Home-page: https://gitlab.com/hestia-earth/hestia-engine-models
|
|
6
6
|
Author: HESTIA Team
|
|
@@ -11,8 +11,8 @@ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.6
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE
|
|
14
|
-
Requires-Dist: hestia-earth-schema
|
|
15
|
-
Requires-Dist: hestia-earth-utils>=0.
|
|
14
|
+
Requires-Dist: hestia-earth-schema<34.0.0,>=33.4.0
|
|
15
|
+
Requires-Dist: hestia-earth-utils>=0.15.1
|
|
16
16
|
Requires-Dist: python-dateutil>=2.8.1
|
|
17
17
|
Requires-Dist: CurrencyConverter==0.16.8
|
|
18
18
|
Requires-Dist: haversine>=2.7.0
|