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.
Files changed (51) hide show
  1. hestia_earth/models/config/Cycle.json +2193 -0
  2. hestia_earth/models/config/ImpactAssessment.json +2041 -0
  3. hestia_earth/models/config/Site.json +471 -0
  4. hestia_earth/models/config/__init__.py +71 -0
  5. hestia_earth/models/config/run-calculations.json +42 -0
  6. hestia_earth/models/config/trigger-calculations.json +43 -0
  7. hestia_earth/models/ipcc2019/animal/hoursWorkedPerDay.py +38 -0
  8. hestia_earth/models/mocking/search-results.json +4524 -27
  9. hestia_earth/models/version.py +1 -1
  10. hestia_earth/orchestrator/__init__.py +40 -0
  11. hestia_earth/orchestrator/log.py +62 -0
  12. hestia_earth/orchestrator/models/__init__.py +118 -0
  13. hestia_earth/orchestrator/models/emissions/__init__.py +0 -0
  14. hestia_earth/orchestrator/models/emissions/deleted.py +15 -0
  15. hestia_earth/orchestrator/models/transformations.py +103 -0
  16. hestia_earth/orchestrator/strategies/__init__.py +0 -0
  17. hestia_earth/orchestrator/strategies/merge/__init__.py +42 -0
  18. hestia_earth/orchestrator/strategies/merge/merge_append.py +29 -0
  19. hestia_earth/orchestrator/strategies/merge/merge_default.py +1 -0
  20. hestia_earth/orchestrator/strategies/merge/merge_list.py +103 -0
  21. hestia_earth/orchestrator/strategies/merge/merge_node.py +59 -0
  22. hestia_earth/orchestrator/strategies/run/__init__.py +8 -0
  23. hestia_earth/orchestrator/strategies/run/add_blank_node_if_missing.py +85 -0
  24. hestia_earth/orchestrator/strategies/run/add_key_if_missing.py +9 -0
  25. hestia_earth/orchestrator/strategies/run/always.py +6 -0
  26. hestia_earth/orchestrator/utils.py +116 -0
  27. {hestia_earth_models-0.65.6.dist-info → hestia_earth_models-0.65.7.dist-info}/METADATA +27 -5
  28. {hestia_earth_models-0.65.6.dist-info → hestia_earth_models-0.65.7.dist-info}/RECORD +51 -7
  29. tests/models/ipcc2019/animal/test_hoursWorkedPerDay.py +22 -0
  30. tests/models/test_config.py +115 -0
  31. tests/orchestrator/__init__.py +0 -0
  32. tests/orchestrator/models/__init__.py +0 -0
  33. tests/orchestrator/models/emissions/__init__.py +0 -0
  34. tests/orchestrator/models/emissions/test_deleted.py +21 -0
  35. tests/orchestrator/models/test_transformations.py +29 -0
  36. tests/orchestrator/strategies/__init__.py +0 -0
  37. tests/orchestrator/strategies/merge/__init__.py +0 -0
  38. tests/orchestrator/strategies/merge/test_merge_append.py +33 -0
  39. tests/orchestrator/strategies/merge/test_merge_default.py +7 -0
  40. tests/orchestrator/strategies/merge/test_merge_list.py +327 -0
  41. tests/orchestrator/strategies/merge/test_merge_node.py +95 -0
  42. tests/orchestrator/strategies/run/__init__.py +0 -0
  43. tests/orchestrator/strategies/run/test_add_blank_node_if_missing.py +114 -0
  44. tests/orchestrator/strategies/run/test_add_key_if_missing.py +14 -0
  45. tests/orchestrator/strategies/run/test_always.py +5 -0
  46. tests/orchestrator/test_models.py +69 -0
  47. tests/orchestrator/test_orchestrator.py +27 -0
  48. tests/orchestrator/test_utils.py +109 -0
  49. {hestia_earth_models-0.65.6.dist-info → hestia_earth_models-0.65.7.dist-info}/LICENSE +0 -0
  50. {hestia_earth_models-0.65.6.dist-info → hestia_earth_models-0.65.7.dist-info}/WHEEL +0 -0
  51. {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,6 @@
1
+ from hestia_earth.orchestrator.log import logShouldRun
2
+
3
+
4
+ def should_run(data: dict, model: dict):
5
+ logShouldRun(data, model.get('model'), None, True, key=model.get('key'), value=model.get('value'))
6
+ return True
@@ -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.6
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.14
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
- ### Using Spatial Models
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
- ### Using the ecoinventV3 model
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=7xFo8Se5gLwBCboxTmtPAStKLrqZnJgu1-olEArI-44,19
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=Zc6x4KxDAOvez6v5ZhwLxUB_IKghXr8kWX2TUJ8wAFU,11955
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
- hestia_earth_models-0.65.6.dist-info/LICENSE,sha256=TD25LoiRJsA5CPUNrcyt1PXlGcbUGFMAeZoBcfCrCNE,1154
1164
- hestia_earth_models-0.65.6.dist-info/METADATA,sha256=AqVhpmA7vBW5Tu5lG1ovfvzdbF6QjX6IIodiV6_-JlU,3344
1165
- hestia_earth_models-0.65.6.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
1166
- hestia_earth_models-0.65.6.dist-info/top_level.txt,sha256=1dqA9TqpOLTEgpqa-YBsmbCmmNU1y56AtfFGEceZ2A0,19
1167
- hestia_earth_models-0.65.6.dist-info/RECORD,,
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]
@@ -0,0 +1,7 @@
1
+ from hestia_earth.orchestrator.strategies.merge.merge_default import merge
2
+
3
+
4
+ def test_should_merge():
5
+ source = {'value': [100]}
6
+ dest = {'value': [50]}
7
+ assert merge(source, dest) == dest