hestia-earth-models 0.65.6__py3-none-any.whl → 0.65.7__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.
- hestia_earth/models/config/Cycle.json +2193 -0
- hestia_earth/models/config/ImpactAssessment.json +2041 -0
- hestia_earth/models/config/Site.json +471 -0
- hestia_earth/models/config/__init__.py +71 -0
- hestia_earth/models/config/run-calculations.json +42 -0
- hestia_earth/models/config/trigger-calculations.json +43 -0
- hestia_earth/models/ipcc2019/animal/hoursWorkedPerDay.py +38 -0
- hestia_earth/models/mocking/search-results.json +4524 -27
- hestia_earth/models/version.py +1 -1
- hestia_earth/orchestrator/__init__.py +40 -0
- hestia_earth/orchestrator/log.py +62 -0
- hestia_earth/orchestrator/models/__init__.py +118 -0
- hestia_earth/orchestrator/models/emissions/__init__.py +0 -0
- hestia_earth/orchestrator/models/emissions/deleted.py +15 -0
- hestia_earth/orchestrator/models/transformations.py +103 -0
- hestia_earth/orchestrator/strategies/__init__.py +0 -0
- hestia_earth/orchestrator/strategies/merge/__init__.py +42 -0
- hestia_earth/orchestrator/strategies/merge/merge_append.py +29 -0
- hestia_earth/orchestrator/strategies/merge/merge_default.py +1 -0
- hestia_earth/orchestrator/strategies/merge/merge_list.py +103 -0
- hestia_earth/orchestrator/strategies/merge/merge_node.py +59 -0
- hestia_earth/orchestrator/strategies/run/__init__.py +8 -0
- hestia_earth/orchestrator/strategies/run/add_blank_node_if_missing.py +85 -0
- hestia_earth/orchestrator/strategies/run/add_key_if_missing.py +9 -0
- hestia_earth/orchestrator/strategies/run/always.py +6 -0
- hestia_earth/orchestrator/utils.py +116 -0
- {hestia_earth_models-0.65.6.dist-info → hestia_earth_models-0.65.7.dist-info}/METADATA +27 -5
- {hestia_earth_models-0.65.6.dist-info → hestia_earth_models-0.65.7.dist-info}/RECORD +51 -7
- tests/models/ipcc2019/animal/test_hoursWorkedPerDay.py +22 -0
- tests/models/test_config.py +115 -0
- tests/orchestrator/__init__.py +0 -0
- tests/orchestrator/models/__init__.py +0 -0
- tests/orchestrator/models/emissions/__init__.py +0 -0
- tests/orchestrator/models/emissions/test_deleted.py +21 -0
- tests/orchestrator/models/test_transformations.py +29 -0
- tests/orchestrator/strategies/__init__.py +0 -0
- tests/orchestrator/strategies/merge/__init__.py +0 -0
- tests/orchestrator/strategies/merge/test_merge_append.py +33 -0
- tests/orchestrator/strategies/merge/test_merge_default.py +7 -0
- tests/orchestrator/strategies/merge/test_merge_list.py +327 -0
- tests/orchestrator/strategies/merge/test_merge_node.py +95 -0
- tests/orchestrator/strategies/run/__init__.py +0 -0
- tests/orchestrator/strategies/run/test_add_blank_node_if_missing.py +114 -0
- tests/orchestrator/strategies/run/test_add_key_if_missing.py +14 -0
- tests/orchestrator/strategies/run/test_always.py +5 -0
- tests/orchestrator/test_models.py +69 -0
- tests/orchestrator/test_orchestrator.py +27 -0
- tests/orchestrator/test_utils.py +109 -0
- {hestia_earth_models-0.65.6.dist-info → hestia_earth_models-0.65.7.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.65.6.dist-info → hestia_earth_models-0.65.7.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.65.6.dist-info → hestia_earth_models-0.65.7.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,85 @@
|
|
1
|
+
from hestia_earth.utils.lookup import download_lookup, get_table_value, column_name
|
2
|
+
from hestia_earth.utils.api import download_hestia
|
3
|
+
|
4
|
+
from hestia_earth.orchestrator.log import debugValues, logShouldRun
|
5
|
+
from hestia_earth.orchestrator.utils import get_required_model_param, find_term_match
|
6
|
+
|
7
|
+
_ALLOW_ALL = 'all'
|
8
|
+
|
9
|
+
|
10
|
+
def _lookup_values(term: dict, column: str):
|
11
|
+
term_id = term.get('@id')
|
12
|
+
term_type = term.get('termType')
|
13
|
+
lookup = download_lookup(f"{term_type}.csv")
|
14
|
+
values = get_table_value(lookup, 'termid', term_id, column_name(column))
|
15
|
+
return (values or _ALLOW_ALL).split(';')
|
16
|
+
|
17
|
+
|
18
|
+
def _is_node_type_allowed(data: dict, term_id: str):
|
19
|
+
node_type = data.get('@type', data.get('type'))
|
20
|
+
term = download_hestia(term_id)
|
21
|
+
allowed_types = _lookup_values(term, 'typesAllowed') if term else [_ALLOW_ALL]
|
22
|
+
return True if _ALLOW_ALL in allowed_types or not node_type else node_type in allowed_types
|
23
|
+
|
24
|
+
|
25
|
+
def _run_required(data: dict, model: str, term_id: str):
|
26
|
+
node_type_allowed = _is_node_type_allowed(data, term_id)
|
27
|
+
|
28
|
+
run_required = all([node_type_allowed])
|
29
|
+
debugValues(data, model=model, term=term_id,
|
30
|
+
run_required=run_required,
|
31
|
+
node_type_allowed=node_type_allowed)
|
32
|
+
return run_required
|
33
|
+
|
34
|
+
|
35
|
+
_RUN_FROM_ARGS = {
|
36
|
+
'runNonAddedTerm': lambda node: 'term' not in node.get('added', []),
|
37
|
+
'runNonMeasured': lambda node: node.get('methodTier') != 'measured'
|
38
|
+
}
|
39
|
+
|
40
|
+
|
41
|
+
def _is_empty(node: dict, skip_empty_value: bool = False):
|
42
|
+
return node is None or all([
|
43
|
+
not skip_empty_value,
|
44
|
+
node.get('value') is None or node.get('value') == []
|
45
|
+
])
|
46
|
+
|
47
|
+
|
48
|
+
def _run_aggregated(data: dict, skip_aggregated: bool = False):
|
49
|
+
return not data.get('aggregated', False) or not skip_aggregated
|
50
|
+
|
51
|
+
|
52
|
+
def _is_0_emission(node: dict):
|
53
|
+
# emissions are set to 0 when not required to run, but we should still run for every model
|
54
|
+
return node is not None and all([
|
55
|
+
node.get('@type', node.get('type')) == 'Emission',
|
56
|
+
'value' in node.get('added', [])
|
57
|
+
]) and node.get('value', [])[0] == 0
|
58
|
+
|
59
|
+
|
60
|
+
def should_run(data: dict, model: dict):
|
61
|
+
key = get_required_model_param(model, 'key')
|
62
|
+
term_id = get_required_model_param(model, 'value')
|
63
|
+
args = model.get('runArgs', {})
|
64
|
+
node = find_term_match(data.get(key, []), args.get('termId', term_id), None)
|
65
|
+
|
66
|
+
# run if: value is empty or force run from args
|
67
|
+
is_empty = _is_empty(node, args.get('skipEmptyValue', False))
|
68
|
+
is_0_emission = _is_0_emission(node)
|
69
|
+
run_is_aggregated = _run_aggregated(data, args.get('skipAggregated', False))
|
70
|
+
run_args = {
|
71
|
+
key: func(node) for key, func in _RUN_FROM_ARGS.items() if node and args.get(key, False) is True
|
72
|
+
}
|
73
|
+
run = any([
|
74
|
+
is_empty,
|
75
|
+
is_0_emission,
|
76
|
+
(len(run_args.keys()) > 0 and all([v for _k, v in run_args.items()]))
|
77
|
+
]) and _run_required(data, model.get('model'), term_id) and run_is_aggregated
|
78
|
+
|
79
|
+
logShouldRun(data, model.get('model'), term_id, run, key=key, value=term_id,
|
80
|
+
is_empty=is_empty,
|
81
|
+
is_0_emission=is_0_emission,
|
82
|
+
run_is_aggregated=run_is_aggregated,
|
83
|
+
**run_args)
|
84
|
+
|
85
|
+
return run
|
@@ -0,0 +1,9 @@
|
|
1
|
+
from hestia_earth.orchestrator.log import logShouldRun
|
2
|
+
from hestia_earth.orchestrator.utils import get_required_model_param
|
3
|
+
|
4
|
+
|
5
|
+
def should_run(data: dict, model: dict):
|
6
|
+
key = get_required_model_param(model, 'key')
|
7
|
+
run = data.get(key) is None
|
8
|
+
logShouldRun(data, model.get('model'), None, run, key=key, value=model.get('value'))
|
9
|
+
return run
|
@@ -0,0 +1,116 @@
|
|
1
|
+
from typing import Union
|
2
|
+
import re
|
3
|
+
from statistics import mean
|
4
|
+
from functools import reduce
|
5
|
+
|
6
|
+
EXCLUDED_VERSION_KEYS = [
|
7
|
+
'@type'
|
8
|
+
]
|
9
|
+
|
10
|
+
|
11
|
+
def get_required_model_param(model, key: str):
|
12
|
+
if key not in model:
|
13
|
+
raise KeyError(f"Missing required '{key}' in model")
|
14
|
+
return model[key]
|
15
|
+
|
16
|
+
|
17
|
+
def _lowercase(string): return str(string).lower()
|
18
|
+
|
19
|
+
|
20
|
+
def _snakecase(string):
|
21
|
+
string = re.sub(r"[\-\.\s]", '_', str(string))
|
22
|
+
if not string:
|
23
|
+
return string
|
24
|
+
return _lowercase(string[0]) + re.sub(r"[A-Z]", lambda matched: '_' + _lowercase(matched.group(0)), string[1:])
|
25
|
+
|
26
|
+
|
27
|
+
def _average(value, default=0): return mean(value) if value is not None and isinstance(value, list) else default
|
28
|
+
|
29
|
+
|
30
|
+
def find_term_match(values: list, term_id: str, default_val={}):
|
31
|
+
return next((v for v in values if v.get('term', {}).get('@id') == term_id), default_val)
|
32
|
+
|
33
|
+
|
34
|
+
def _non_empty(value): return value != '' and value is not None and value != []
|
35
|
+
|
36
|
+
|
37
|
+
def _non_empty_list(values):
|
38
|
+
return list(filter(_non_empty, values)) if isinstance(values, list) else _non_empty(values)
|
39
|
+
|
40
|
+
|
41
|
+
def _filter_by_keys(values, keys: list): return {key: values[key] for key in keys if values.get(key) is not None}
|
42
|
+
|
43
|
+
|
44
|
+
_SKIP_KEYS = ['added', 'addedVersion', 'updated', 'updatedVersion']
|
45
|
+
|
46
|
+
|
47
|
+
def _update_key_version(version: str, node: dict, key: str, is_update=True):
|
48
|
+
def update(field: str):
|
49
|
+
if key not in _SKIP_KEYS:
|
50
|
+
if key in node.get(field, []):
|
51
|
+
node.get(f"{field}Version")[node[field].index(key)] = version
|
52
|
+
else:
|
53
|
+
node[field] = node.get(field, []) + [key]
|
54
|
+
node[f"{field}Version"] = node.get(f"{field}Version", []) + [version]
|
55
|
+
return node
|
56
|
+
|
57
|
+
return update('updated' if is_update else 'added')
|
58
|
+
|
59
|
+
|
60
|
+
def _safe_deep_update_list_version(version: str, new_data: list, prev_data: list, index: int):
|
61
|
+
try:
|
62
|
+
new_data[index] = update_node_version(version, new_data[index], prev_data[index])
|
63
|
+
except Exception:
|
64
|
+
try:
|
65
|
+
# try again with an empty value as old data
|
66
|
+
new_data[index] = update_node_version(version, new_data[index], {})
|
67
|
+
except Exception:
|
68
|
+
pass
|
69
|
+
|
70
|
+
|
71
|
+
def _deep_update_node_version(version: str, new_data: Union[dict, list], prev_data: Union[dict, list]):
|
72
|
+
if isinstance(new_data, list) and all([isinstance(v, dict) for v in new_data]):
|
73
|
+
for index, v in enumerate(new_data):
|
74
|
+
_safe_deep_update_list_version(version, new_data, prev_data, index)
|
75
|
+
if isinstance(new_data, dict):
|
76
|
+
new_data = update_node_version(version, new_data, prev_data)
|
77
|
+
|
78
|
+
|
79
|
+
def update_node_version(version: str, new_data: dict, prev_data: dict = {}):
|
80
|
+
"""
|
81
|
+
Update the node `added` and `updated` fields by comparing the previous fields with the new ones.
|
82
|
+
The version of the model adding/updating the fields will be used by default.
|
83
|
+
|
84
|
+
Parameters
|
85
|
+
----------
|
86
|
+
version : str
|
87
|
+
The version to use in the `addedVersion` or `updatedVersion` field.
|
88
|
+
new_data : dict
|
89
|
+
The new data.
|
90
|
+
prev_data : dict
|
91
|
+
Optional - the previous data. If not set, a default empty dictionary will be used as previous value,
|
92
|
+
so every field will be marked as "added".
|
93
|
+
|
94
|
+
Returns
|
95
|
+
-------
|
96
|
+
dict
|
97
|
+
The new data with additional `added`, `addedVersion`, `updated` and `updatedVersion` fields.
|
98
|
+
"""
|
99
|
+
def update(prev, key):
|
100
|
+
# TODO: do a better comparison
|
101
|
+
is_updated = key in prev_data and prev_data.get(key) != new_data.get(key)
|
102
|
+
is_added = key not in prev_data
|
103
|
+
value = _update_key_version(version, prev, key, is_updated) if is_updated or is_added else prev
|
104
|
+
_deep_update_node_version(version, new_data.get(key), prev_data.get(key))
|
105
|
+
return value
|
106
|
+
|
107
|
+
keys = [key for key in new_data.keys() if key not in EXCLUDED_VERSION_KEYS]
|
108
|
+
return new_data if any([
|
109
|
+
prev_data is None,
|
110
|
+
# do not add fields on Term
|
111
|
+
(prev_data or {}).get('@type') == 'Term',
|
112
|
+
new_data.get('@type') == 'Term'
|
113
|
+
]) else reduce(update, keys, new_data)
|
114
|
+
|
115
|
+
|
116
|
+
def new_practice(term: dict): return {'@type': 'Practice', 'term': term}
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: hestia-earth-models
|
3
|
-
Version: 0.65.
|
3
|
+
Version: 0.65.7
|
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
|
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python :: 3.6
|
|
12
12
|
Description-Content-Type: text/markdown
|
13
13
|
License-File: LICENSE
|
14
14
|
Requires-Dist: hestia-earth-schema==30.*
|
15
|
-
Requires-Dist: hestia-earth-utils>=0.13.
|
15
|
+
Requires-Dist: hestia-earth-utils>=0.13.16
|
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
|
@@ -49,10 +49,14 @@ WEB_URL=https://hestia.earth
|
|
49
49
|
```python
|
50
50
|
from hestia_earth.models.pooreNemecek2018 import run
|
51
51
|
|
52
|
+
cycle_data = {"@type": "Cycle", ...}
|
52
53
|
# cycle is a JSONLD node Cycle
|
53
|
-
run('no3ToGroundwaterSoilFlux', cycle_data)
|
54
|
+
result = run('no3ToGroundwaterSoilFlux', cycle_data)
|
55
|
+
print(result)
|
54
56
|
```
|
55
57
|
|
58
|
+
This will display only the result of the `no3ToGroundwaterSoilFlux` model (Emission).
|
59
|
+
|
56
60
|
Additionally, to reduce the number of queries to the HESTIA API and run the models faster, prefetching can be enabled:
|
57
61
|
```python
|
58
62
|
from hestia_earth.models.preload_requests import enable_preload
|
@@ -60,13 +64,31 @@ from hestia_earth.models.preload_requests import enable_preload
|
|
60
64
|
enable_preload()
|
61
65
|
```
|
62
66
|
|
63
|
-
|
67
|
+
#### Using the orchestrator
|
68
|
+
|
69
|
+
The models come with an "orchestrator", which allows you to run a pre-configured set of models instead of a single one.
|
70
|
+
|
71
|
+
The configuration for each Node (Cycle, Site or ImpactAssessment) can be found in the [config](./config) folder.
|
72
|
+
|
73
|
+
Usage:
|
74
|
+
```python
|
75
|
+
from hestia_earth.orchestrator import run
|
76
|
+
from hestia_earth.models.config import load_config
|
77
|
+
|
78
|
+
cycle_data = {"@type": "Cycle", ...}
|
79
|
+
result = run(cycle, load_config(cycle))
|
80
|
+
print(result)
|
81
|
+
```
|
82
|
+
|
83
|
+
This will display the Cycle recalculated with all HESTIA default models running.
|
84
|
+
|
85
|
+
#### Using Spatial Models
|
64
86
|
|
65
87
|
We have models that can gap-fill geographical information on a `Site`. If you want to use these models:
|
66
88
|
1. Install the library: `pip install hestia_earth.earth_engine`
|
67
89
|
2. Follow the [Getting Started instructions](https://gitlab.com/hestia-earth/hestia-earth-engine#getting-started).
|
68
90
|
|
69
|
-
|
91
|
+
#### Using the ecoinventV3 model
|
70
92
|
|
71
93
|
ecoinvent is a consistent, transparent, and well validated life cycle inventory database.
|
72
94
|
We use ecoinvent data to ascertain the environmental impacts of activities that occur outside of our system boundary, for example data on the environmental impacts of extracting oil and producing diesel, or the impacts of manufacturing plastics.
|
@@ -4,7 +4,7 @@ hestia_earth/models/cache_sites.py,sha256=Llo2SH1Lp-R8x1JRxJ2Ta-vw5RbdUj2FHXUP-c
|
|
4
4
|
hestia_earth/models/log.py,sha256=_zAfyOkL_VknEnMFvcpvenSMghadlDfZhiSx28545Gk,3558
|
5
5
|
hestia_earth/models/preload_requests.py,sha256=vK_G1UzhNMhYy7ymnCtHUz_vv3cfApCSKqv29VREEBQ,1943
|
6
6
|
hestia_earth/models/requirements.py,sha256=eU4yT443fx7BnaokhrLB_PCizJI7Y6m4auyo8vQauNg,17363
|
7
|
-
hestia_earth/models/version.py,sha256=
|
7
|
+
hestia_earth/models/version.py,sha256=stR4pjKHfSb4U3BpRBryv63I9rlfGch2ifjxGb-dZps,19
|
8
8
|
hestia_earth/models/agribalyse2016/__init__.py,sha256=WvK0qCQbnYtg9oZxrACd1wGormZyXibPtpCnIQeDqbw,415
|
9
9
|
hestia_earth/models/agribalyse2016/fuelElectricity.py,sha256=rm5ZaRAzJ08m2y4BxkGh-RjudkDWgozmg3XumoRm-fQ,4511
|
10
10
|
hestia_earth/models/agribalyse2016/machineryInfrastructureDepreciatedAmountPerCycle.py,sha256=BPjnWmg73i_OxM2ouCdMTWZtPIqyoUAXrvutntyteE0,3390
|
@@ -34,6 +34,12 @@ hestia_earth/models/cml2001Baseline/terrestrialAcidificationPotentialIncludingFa
|
|
34
34
|
hestia_earth/models/cml2001NonBaseline/__init__.py,sha256=vI8wp8Og_e8DiJqYYvp33YoI3t4ffAC31LWlnV20JTg,419
|
35
35
|
hestia_earth/models/cml2001NonBaseline/eutrophicationPotentialIncludingFateAverageEurope.py,sha256=lcgyRHY08KCBFPERJNqV4DYGEJCvyHBDnJXD0kEkVqM,1097
|
36
36
|
hestia_earth/models/cml2001NonBaseline/terrestrialAcidificationPotentialExcludingFate.py,sha256=xcrxfs9UoV_EWvV-XzMt35oPWCUsTzqg2SGA3j2MFIw,1091
|
37
|
+
hestia_earth/models/config/Cycle.json,sha256=JSrcDhzYLyQ1M7oDfY39pxOgWCScz3dXFSjNNPWvMTo,56546
|
38
|
+
hestia_earth/models/config/ImpactAssessment.json,sha256=EB8O8_GZ182upCP-Rpko7I48Tdf30ScK-ZZ3rf4DQQI,57585
|
39
|
+
hestia_earth/models/config/Site.json,sha256=FfuME8DLLyoHYJ2uBgnueTIK9E7m9aV7iPT8TBoqlzk,12565
|
40
|
+
hestia_earth/models/config/__init__.py,sha256=UZZdwfnxTqnZLG4hNecu6sfKvMLvctjdWFraE_9H438,2130
|
41
|
+
hestia_earth/models/config/run-calculations.json,sha256=c-WhY3Rd6UinTxz9ht-1O5_rwe2L7DmX6tFaiVGJ0VY,615
|
42
|
+
hestia_earth/models/config/trigger-calculations.json,sha256=pAlb_6GN1HVv9OZwQr8togx7y2ygabGmisJLWILMq_A,623
|
37
43
|
hestia_earth/models/cycle/__init__.py,sha256=VowO3kOHb0LpURsljNaJsYO7s6vgjhul6bF_85UjUEI,406
|
38
44
|
hestia_earth/models/cycle/aboveGroundCropResidueTotal.py,sha256=9swq4YEeJQ2YjVOmghgBYWkMZWdNU4MKCUBY5FsmBSU,3088
|
39
45
|
hestia_earth/models/cycle/coldCarcassWeightPerHead.py,sha256=fQ7huuxyS5PQkRmR_tRCOz9rV3LJwLfLQJjH_TcTz6k,2955
|
@@ -284,6 +290,7 @@ hestia_earth/models/ipcc2019/pastureGrass_utils.py,sha256=nL31uS3c77PH_5nA2E2MvB
|
|
284
290
|
hestia_earth/models/ipcc2019/utils.py,sha256=MSDMu15D9DnilFUgi4_6jYXC0FaKso3OODauGTMB6hs,6229
|
285
291
|
hestia_earth/models/ipcc2019/animal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
286
292
|
hestia_earth/models/ipcc2019/animal/fatContent.py,sha256=T-wWzxzPdy4pexS8ivQSeH9Z6U6qRGYfCiEwQDujcww,1003
|
293
|
+
hestia_earth/models/ipcc2019/animal/hoursWorkedPerDay.py,sha256=f-zcslXkbIY9ef-pia2PFmxPEDWiMgp4wxJafSGFLe0,985
|
287
294
|
hestia_earth/models/ipcc2019/animal/liveweightGain.py,sha256=UElmAdB4yQEFAOT5RrURn0Yt7U3gB2qiCWvNXyLk8Hw,971
|
288
295
|
hestia_earth/models/ipcc2019/animal/liveweightPerHead.py,sha256=cq88VTxPYVPyEr2NLPb0Zmmppn_eMp9Xtw6Nxnta4-M,984
|
289
296
|
hestia_earth/models/ipcc2019/animal/milkYieldPerAnimal.py,sha256=XxgSvQLi1Xu6ZpUg1a-7OFZEkItkUK-nVPTDxApUnuY,2777
|
@@ -399,7 +406,7 @@ hestia_earth/models/linkedImpactAssessment/utils.py,sha256=S1zlux02gU2Lajrtoq-zQ
|
|
399
406
|
hestia_earth/models/mocking/__init__.py,sha256=9VX50c-grz-snfd-7MBS0KfF7AadtbKuj7kK6PqtsgE,687
|
400
407
|
hestia_earth/models/mocking/build_mock_search.py,sha256=p15ccEUmkmLp1RiGNznxMz3OFHbI8P1-29ExuohiQN8,1355
|
401
408
|
hestia_earth/models/mocking/mock_search.py,sha256=ccFe_WrI73JElFmxp4hPNLCX7eeU--lBC1JFR901KJY,1069
|
402
|
-
hestia_earth/models/mocking/search-results.json,sha256=
|
409
|
+
hestia_earth/models/mocking/search-results.json,sha256=D2Ule1jJAIeLu4ojLX0pXd4YmUDGxW1BduwqcDbpcUw,102056
|
403
410
|
hestia_earth/models/pooreNemecek2018/__init__.py,sha256=nPboL7ULJzL5nJD5q7q9VOZt_fxbKVm8fmn1Az5YkVY,417
|
404
411
|
hestia_earth/models/pooreNemecek2018/aboveGroundCropResidueTotal.py,sha256=Qt-mel4dkhK6N5uUOutNOinCTFjbjtGzITaaI0LvYc4,2396
|
405
412
|
hestia_earth/models/pooreNemecek2018/belowGroundCropResidue.py,sha256=JT0RybbvWVlo01FO8K0Yj41HrEaJT3Kj1xfayr2X-xw,2315
|
@@ -604,8 +611,26 @@ hestia_earth/models/utils/time_series.py,sha256=vuv033qUZ1gAw3T3wlLNG7vJmXCCsHEz
|
|
604
611
|
hestia_earth/models/utils/transformation.py,sha256=nyT5Mz4_VgFwhkL8JoNX9kxxow0zuxzsYl3W8xOu2p0,370
|
605
612
|
hestia_earth/models/webbEtAl2012AndSintermannEtAl2012/__init__.py,sha256=Niv7ZFMBCwThlbCKGOwA17QdkpOUDFrqrFItGNqnZAA,434
|
606
613
|
hestia_earth/models/webbEtAl2012AndSintermannEtAl2012/nh3ToAirOrganicFertiliser.py,sha256=TGXyusrRd9shT842iqbrI6MkQhICgw7uYdrl4jsDrg8,4193
|
614
|
+
hestia_earth/orchestrator/__init__.py,sha256=ntPWzdomHMdKejrnUlVPCUrLw0P2C9UIt3jRJD_Gwn4,1402
|
615
|
+
hestia_earth/orchestrator/log.py,sha256=rvuc221TZCXB1s_Qxme_lTPAI9cZWkmTvnZHGqSDtWY,2214
|
616
|
+
hestia_earth/orchestrator/utils.py,sha256=LAMUTyIQ-90TR6CUljWPbCNBsAMeukOhW4zoPy7LzuU,4111
|
617
|
+
hestia_earth/orchestrator/models/__init__.py,sha256=wVFqISTf8dKAjsvL9bcqxZx7szn8Gycvuey7Dkxg0fM,4126
|
618
|
+
hestia_earth/orchestrator/models/transformations.py,sha256=zJwfVXabudLXhdyz0Hsk4IV_2OjgMtaYEZbD9kuZtLk,4128
|
619
|
+
hestia_earth/orchestrator/models/emissions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
620
|
+
hestia_earth/orchestrator/models/emissions/deleted.py,sha256=KC6J1bdMJC1rBFlWW5SD1NhbN8kyBPFdncbtY9OfYBU,575
|
621
|
+
hestia_earth/orchestrator/strategies/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
622
|
+
hestia_earth/orchestrator/strategies/merge/__init__.py,sha256=9fytXEII6aSbc0edEp0Dg1QMVayU3eMUXdwKYVky_24,1416
|
623
|
+
hestia_earth/orchestrator/strategies/merge/merge_append.py,sha256=5xJ8fqu2UqCDotVkSxj7yRDRdw0RM2tERsA4j_1Zlu8,915
|
624
|
+
hestia_earth/orchestrator/strategies/merge/merge_default.py,sha256=ssKq5ZIoQr4k2HHpkyPqHJSQQj-AGqu8zUzEQIRafv8,45
|
625
|
+
hestia_earth/orchestrator/strategies/merge/merge_list.py,sha256=JioJi4HajpiYHtYE7xbTWQHOQtu3pWIo512VtaxSsDw,4067
|
626
|
+
hestia_earth/orchestrator/strategies/merge/merge_node.py,sha256=iAgxHVVR7y2kXtR_pdNzS4Fq-iLmwaqNHXMfjIBG6eE,2622
|
627
|
+
hestia_earth/orchestrator/strategies/run/__init__.py,sha256=At0V8CI4vyiSY-Vh2PHMhTYfnp7vl31gq78RyCeIqJk,307
|
628
|
+
hestia_earth/orchestrator/strategies/run/add_blank_node_if_missing.py,sha256=dfmS2AC_d3LjlBT962grygvHK38I4y12K3bl_CT_zlo,3166
|
629
|
+
hestia_earth/orchestrator/strategies/run/add_key_if_missing.py,sha256=t3U-v87XpbtpsvjA_r0Ftm7MhNkGB0kcUSGFlKBIK_I,352
|
630
|
+
hestia_earth/orchestrator/strategies/run/always.py,sha256=D0In6_kr28s-fgqspawgvj5cgFClxGvepZYqtYsjWVE,217
|
607
631
|
tests/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
608
632
|
tests/models/test_cache_sites.py,sha256=vuEVZh_mkuI2cPotX2GB88lURJm2giOQ3yu2Cn4iLoI,2997
|
633
|
+
tests/models/test_config.py,sha256=WFz1OGhJE6kpcYe3kTPBpv4Jn59yeOCAvz1hqnbI_7o,3113
|
609
634
|
tests/models/test_ecoinventV3.py,sha256=_BqfWiYFaw-Y7A-EeabHEnja3d7yb4Ed7gGGvu3Srpw,1936
|
610
635
|
tests/models/test_ecoinventV3AndEmberClimate.py,sha256=_EOxdrdavXP6L5_LtvaVbXb_-56UJXSaiPhpGntmwVc,801
|
611
636
|
tests/models/test_emissionNotRelevant.py,sha256=YXTdRfcdR_JepHuj2P3Y3r0aFMKNOmsXQHY48tmLTQo,1316
|
@@ -872,6 +897,7 @@ tests/models/ipcc2019/test_organicCarbonPerHa_utils.py,sha256=Zd2QlN_Q3k9djuByOH
|
|
872
897
|
tests/models/ipcc2019/test_pastureGrass.py,sha256=mKx8NnTtMT9TrXxRNLv73wD1TWBaiRZzA1xh2ukb-HI,2667
|
873
898
|
tests/models/ipcc2019/animal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
874
899
|
tests/models/ipcc2019/animal/test_fatContent.py,sha256=Emp8jGudRGA_dJaLMR5Jxsv3Gc57rMAnP0CDqswrmlM,775
|
900
|
+
tests/models/ipcc2019/animal/test_hoursWorkedPerDay.py,sha256=gFgCd5hXS_fBhu1f8hbZCci4uTGdpFLjIuER73LY_4A,782
|
875
901
|
tests/models/ipcc2019/animal/test_liveweightGain.py,sha256=KmRZyrjrXZcgff1QFtfu1WphNuJW_nHx1GguD8xB2ls,779
|
876
902
|
tests/models/ipcc2019/animal/test_liveweightPerHead.py,sha256=nfNAcUEIPQeKyjKYttI5W6hiHBMXLZ9Vbz0nfj81ZvA,782
|
877
903
|
tests/models/ipcc2019/animal/test_milkYieldPerAnimal.py,sha256=98rslTxLk92smiUfxRfxB6kjmQAm6085GV9NqWCGpVo,713
|
@@ -1160,8 +1186,26 @@ tests/models/utils/test_term.py,sha256=M5Sa26v2gzQYbZ4H_fo7DspnaCx__-WtL-MULGapC
|
|
1160
1186
|
tests/models/utils/test_time_series.py,sha256=LMhRPf8rp3nAriKAC-2K3FDkrMWntRTUUCERw7Lt68g,2686
|
1161
1187
|
tests/models/webbEtAl2012AndSintermannEtAl2012/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1162
1188
|
tests/models/webbEtAl2012AndSintermannEtAl2012/test_nh3ToAirOrganicFertiliser.py,sha256=qi2FNXS5Af2WDtm7nq_FsprH3BfCF0XxnE0XHmC4aIY,2244
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1189
|
+
tests/orchestrator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1190
|
+
tests/orchestrator/test_models.py,sha256=v5VnyELmrSEcTOtw4lyk2U0r016z8Xx34EFs0FJm5AA,1972
|
1191
|
+
tests/orchestrator/test_orchestrator.py,sha256=dlO4CKn04m__SZhDtvy1npvQUavVNhdcRe4Unj7wg6g,742
|
1192
|
+
tests/orchestrator/test_utils.py,sha256=Sqysl2ocifLUeSbgGUdeRn0Sof0xVEeH4dgoXfe18yw,3050
|
1193
|
+
tests/orchestrator/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1194
|
+
tests/orchestrator/models/test_transformations.py,sha256=cQdk8rEtSJJr27th8SmacsOpC7BP_YVXDrmFDDY4Iks,1080
|
1195
|
+
tests/orchestrator/models/emissions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1196
|
+
tests/orchestrator/models/emissions/test_deleted.py,sha256=55WOjXR2oeKxdRgXmJg4DgIY3f0asPMvez8b5fkT7LI,767
|
1197
|
+
tests/orchestrator/strategies/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1198
|
+
tests/orchestrator/strategies/merge/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1199
|
+
tests/orchestrator/strategies/merge/test_merge_append.py,sha256=T6_y3qDb4ZmZixzSZMNCYD-dbkIHFfvpayXo1K9-lNA,800
|
1200
|
+
tests/orchestrator/strategies/merge/test_merge_default.py,sha256=iEia74Z7RflmxDZ3XzubN9yR8n5DR_CDSMyLIW1e8PU,198
|
1201
|
+
tests/orchestrator/strategies/merge/test_merge_list.py,sha256=KIObPP7AFT0oOE7669UUnYYMSCHX4fQSrb1VFevkQmk,9081
|
1202
|
+
tests/orchestrator/strategies/merge/test_merge_node.py,sha256=yCaIKFFdJcIANidQBJb95f50OPgm9wwTsuTEzhHumA0,3203
|
1203
|
+
tests/orchestrator/strategies/run/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1204
|
+
tests/orchestrator/strategies/run/test_add_blank_node_if_missing.py,sha256=lGqeebvgAwGathB8NLZ14Js5JV_-KyHueaD6I8IH8mU,3615
|
1205
|
+
tests/orchestrator/strategies/run/test_add_key_if_missing.py,sha256=hKwvk1ohcBVnQUCTiDhRW99J0xEa29BpwFi1KC0yWLE,329
|
1206
|
+
tests/orchestrator/strategies/run/test_always.py,sha256=w5-Dhp6yLzgZGAeMRz3OrqZbbAed9gZ1O266a3z9k7w,134
|
1207
|
+
hestia_earth_models-0.65.7.dist-info/LICENSE,sha256=TD25LoiRJsA5CPUNrcyt1PXlGcbUGFMAeZoBcfCrCNE,1154
|
1208
|
+
hestia_earth_models-0.65.7.dist-info/METADATA,sha256=WBcWi-w_mq-nLFKriqoNmPd5Gb2dbMDGRAttdzZIzn4,4046
|
1209
|
+
hestia_earth_models-0.65.7.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
1210
|
+
hestia_earth_models-0.65.7.dist-info/top_level.txt,sha256=1dqA9TqpOLTEgpqa-YBsmbCmmNU1y56AtfFGEceZ2A0,19
|
1211
|
+
hestia_earth_models-0.65.7.dist-info/RECORD,,
|
@@ -0,0 +1,22 @@
|
|
1
|
+
from unittest.mock import patch
|
2
|
+
import json
|
3
|
+
from tests.utils import fixtures_path, fake_new_property
|
4
|
+
|
5
|
+
from hestia_earth.models.ipcc2019 import MODEL
|
6
|
+
from hestia_earth.models.ipcc2019.animal.hoursWorkedPerDay import TERM_ID, run
|
7
|
+
|
8
|
+
utils_path = '.'.join(['hestia_earth', 'models', MODEL, 'animal', 'utils'])
|
9
|
+
class_path = f"hestia_earth.models.{MODEL}.animal.{TERM_ID}"
|
10
|
+
fixtures_folder = f"{fixtures_path}/{MODEL}/animal/{TERM_ID}"
|
11
|
+
|
12
|
+
|
13
|
+
@patch(f"{utils_path}._new_property", side_effect=fake_new_property)
|
14
|
+
def test_run(*args):
|
15
|
+
with open(f"{fixtures_folder}/cycle.jsonld", encoding='utf-8') as f:
|
16
|
+
cycle = json.load(f)
|
17
|
+
|
18
|
+
with open(f"{fixtures_folder}/result.jsonld", encoding='utf-8') as f:
|
19
|
+
expected = json.load(f)
|
20
|
+
|
21
|
+
value = run(cycle)
|
22
|
+
assert value == expected
|
@@ -0,0 +1,115 @@
|
|
1
|
+
import pytest
|
2
|
+
import importlib
|
3
|
+
from hestia_earth.utils.tools import flatten
|
4
|
+
|
5
|
+
from hestia_earth.models.config import (
|
6
|
+
load_config, config_max_stage, _is_aggregated_model, _remove_aggregated, load_run_config, load_trigger_config
|
7
|
+
)
|
8
|
+
|
9
|
+
|
10
|
+
_aggregated_model = {
|
11
|
+
"value": "input.hestiaAggregatedData"
|
12
|
+
}
|
13
|
+
_other_model = {
|
14
|
+
"value": "otherModel"
|
15
|
+
}
|
16
|
+
|
17
|
+
|
18
|
+
def test_load_config():
|
19
|
+
node_type = 'Cycle'
|
20
|
+
config = load_config(node_type)
|
21
|
+
assert config.get('models') is not None
|
22
|
+
|
23
|
+
|
24
|
+
def test_load_config_error():
|
25
|
+
node_type = 'Unkown'
|
26
|
+
|
27
|
+
with pytest.raises(Exception, match='Invalid type Unkown.'):
|
28
|
+
load_config(node_type)
|
29
|
+
|
30
|
+
|
31
|
+
def test_config_max_stage():
|
32
|
+
node_type = 'Cycle'
|
33
|
+
config = load_config(node_type)
|
34
|
+
assert config_max_stage(config) == 2
|
35
|
+
|
36
|
+
|
37
|
+
def test_is_aggregated_model():
|
38
|
+
assert _is_aggregated_model(_aggregated_model) is True
|
39
|
+
assert not _is_aggregated_model(_other_model)
|
40
|
+
|
41
|
+
|
42
|
+
def test_remove_aggregated():
|
43
|
+
models = [
|
44
|
+
[_aggregated_model, _other_model],
|
45
|
+
_aggregated_model, _other_model
|
46
|
+
]
|
47
|
+
assert _remove_aggregated(models) == [[_other_model], _other_model]
|
48
|
+
|
49
|
+
|
50
|
+
def test_load_config_skip_aggregated_models():
|
51
|
+
node_type = 'Cycle'
|
52
|
+
all_models = load_config(node_type, skip_aggregated_models=False).get('models')
|
53
|
+
models_no_aggregated = load_config(node_type, skip_aggregated_models=True).get('models')
|
54
|
+
assert all_models != models_no_aggregated
|
55
|
+
|
56
|
+
|
57
|
+
def test_load_run_config():
|
58
|
+
assert len(load_run_config(node_type='Site', stage=1)) == 0
|
59
|
+
assert len(load_run_config(node_type='Site', stage=2)) == 1
|
60
|
+
|
61
|
+
|
62
|
+
def test_load_run_config_invalid_stage():
|
63
|
+
with pytest.raises(Exception) as e:
|
64
|
+
load_run_config(
|
65
|
+
node_type='ImpactAssessment',
|
66
|
+
stage=2
|
67
|
+
)
|
68
|
+
assert str(e.value) == 'Invalid stage configuration for ImpactAssessment: 2'
|
69
|
+
|
70
|
+
|
71
|
+
def test_load_trigger_config_config():
|
72
|
+
assert len(load_trigger_config(node_type='Site', stage=1)) == 1
|
73
|
+
assert len(load_trigger_config(node_type='Site', stage=2)) == 1
|
74
|
+
|
75
|
+
|
76
|
+
def test_load_trigger_config_invalid_stage():
|
77
|
+
with pytest.raises(Exception) as e:
|
78
|
+
load_trigger_config(
|
79
|
+
node_type='ImpactAssessment',
|
80
|
+
stage=2
|
81
|
+
)
|
82
|
+
assert str(e.value) == 'Invalid stage configuration for ImpactAssessment: 2'
|
83
|
+
|
84
|
+
|
85
|
+
# included in orchestrator
|
86
|
+
_ignore_models = ['emissions.deleted', 'transformations']
|
87
|
+
_ignore_values = [None, '', 'all']
|
88
|
+
|
89
|
+
|
90
|
+
def _model_path(model: dict):
|
91
|
+
name = model.get('model')
|
92
|
+
value = model.get('value')
|
93
|
+
suffix = f"hestia_earth.models.{name}"
|
94
|
+
return f"{suffix}.{value}" if value not in _ignore_values else suffix
|
95
|
+
|
96
|
+
|
97
|
+
def _get_models_paths(node_type: str):
|
98
|
+
models = flatten(load_config(node_type).get('models', []))
|
99
|
+
return [
|
100
|
+
_model_path(m)
|
101
|
+
for m in models
|
102
|
+
if m.get('model') not in _ignore_models
|
103
|
+
]
|
104
|
+
|
105
|
+
|
106
|
+
@pytest.mark.parametrize(
|
107
|
+
'node_type',
|
108
|
+
['Cycle', 'Site', 'ImpactAssessment']
|
109
|
+
)
|
110
|
+
def test_load_config_cycle(node_type):
|
111
|
+
paths = _get_models_paths(node_type)
|
112
|
+
|
113
|
+
for path in paths:
|
114
|
+
run = importlib.import_module(path).run
|
115
|
+
assert run is not None, path
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,21 @@
|
|
1
|
+
from unittest.mock import patch
|
2
|
+
import json
|
3
|
+
import os
|
4
|
+
|
5
|
+
from tests.utils import fixtures_path
|
6
|
+
from hestia_earth.orchestrator.models.emissions.deleted import run
|
7
|
+
|
8
|
+
folder_path = os.path.join(fixtures_path, 'orchestrator', 'emissions', 'deleted')
|
9
|
+
|
10
|
+
|
11
|
+
@patch('hestia_earth.orchestrator.strategies.merge._merge_version', return_value='0.0.0')
|
12
|
+
def test_run(*args):
|
13
|
+
with open(os.path.join(folder_path, 'config.json'), encoding='utf-8') as f:
|
14
|
+
config = json.load(f)
|
15
|
+
with open(os.path.join(folder_path, 'cycle.jsonld'), encoding='utf-8') as f:
|
16
|
+
cycle = json.load(f)
|
17
|
+
with open(os.path.join(folder_path, 'result.jsonld'), encoding='utf-8') as f:
|
18
|
+
expected = json.load(f)
|
19
|
+
|
20
|
+
result = run(config.get('models'), cycle)
|
21
|
+
assert result == expected
|
@@ -0,0 +1,29 @@
|
|
1
|
+
from unittest.mock import patch
|
2
|
+
import json
|
3
|
+
import os
|
4
|
+
|
5
|
+
from tests.utils import fixtures_path
|
6
|
+
from hestia_earth.orchestrator.models.transformations import run, _include_practice
|
7
|
+
|
8
|
+
folder_path = os.path.join(fixtures_path, 'orchestrator', 'transformation')
|
9
|
+
|
10
|
+
|
11
|
+
@patch('hestia_earth.orchestrator.strategies.merge._merge_version', return_value='0.0.0')
|
12
|
+
def test_run(*args):
|
13
|
+
with open(os.path.join(folder_path, 'config.json'), encoding='utf-8') as f:
|
14
|
+
config = json.load(f)
|
15
|
+
with open(os.path.join(folder_path, 'cycle.jsonld'), encoding='utf-8') as f:
|
16
|
+
cycle = json.load(f)
|
17
|
+
with open(os.path.join(folder_path, 'result.jsonld'), encoding='utf-8') as f:
|
18
|
+
expected = json.load(f)
|
19
|
+
|
20
|
+
result = run(config.get('models'), cycle)
|
21
|
+
assert result == expected
|
22
|
+
|
23
|
+
|
24
|
+
def test_include_practice():
|
25
|
+
term = {'@id': 'genericCropProduct', 'termType': 'crop'}
|
26
|
+
assert not _include_practice({'term': term})
|
27
|
+
|
28
|
+
term = {'@id': 'yieldOfPrimaryAquacultureProductLiveweightPerM2', 'termType': 'aquacultureManagement'}
|
29
|
+
assert _include_practice({'term': term}) is True
|
File without changes
|
File without changes
|
@@ -0,0 +1,33 @@
|
|
1
|
+
from unittest.mock import patch
|
2
|
+
|
3
|
+
from hestia_earth.orchestrator.strategies.merge.merge_append import merge
|
4
|
+
|
5
|
+
class_path = 'hestia_earth.orchestrator.strategies.merge.merge_append'
|
6
|
+
|
7
|
+
|
8
|
+
@patch(f"{class_path}.update_node_version", side_effect=lambda _v, n: n)
|
9
|
+
def test_merge_new_node(*args):
|
10
|
+
node1 = {
|
11
|
+
'term': {'@id': 'term-1'},
|
12
|
+
'value': 1
|
13
|
+
}
|
14
|
+
node2 = {
|
15
|
+
'term': {'@id': 'term-2'},
|
16
|
+
'value': 2
|
17
|
+
}
|
18
|
+
source = [node1]
|
19
|
+
result = merge(source, [node1, node2], '1')
|
20
|
+
result = merge(result, node2, '1')
|
21
|
+
assert result == [node1, node1, node2, node2]
|
22
|
+
|
23
|
+
|
24
|
+
def test_merge_list():
|
25
|
+
source = [1]
|
26
|
+
dest = [2, 3]
|
27
|
+
assert merge(source, dest, '1') == [1, 2, 3]
|
28
|
+
|
29
|
+
|
30
|
+
def test_merge_el():
|
31
|
+
source = [1]
|
32
|
+
dest = 2
|
33
|
+
assert merge(source, dest, '1') == [1, 2]
|