hestia-earth-models 0.59.0__py3-none-any.whl → 0.59.2__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/cycle/irrigatedTypeUnspecified.py +6 -7
- hestia_earth/models/impact_assessment/irrigated.py +4 -1
- hestia_earth/models/ipcc2006/n2OToAirCropResidueDecompositionIndirect.py +3 -3
- hestia_earth/models/ipcc2006/n2OToAirExcretaIndirect.py +3 -3
- hestia_earth/models/ipcc2006/n2OToAirInorganicFertiliserIndirect.py +5 -4
- hestia_earth/models/ipcc2006/n2OToAirOrganicFertiliserIndirect.py +5 -4
- hestia_earth/models/ipcc2006/utils.py +0 -16
- hestia_earth/models/ipcc2019/n2OToAirCropResidueDecompositionDirect.py +8 -7
- hestia_earth/models/ipcc2019/n2OToAirCropResidueDecompositionIndirect.py +100 -0
- hestia_earth/models/ipcc2019/n2OToAirExcretaIndirect.py +100 -0
- hestia_earth/models/ipcc2019/n2OToAirInorganicFertiliserIndirect.py +54 -61
- hestia_earth/models/ipcc2019/n2OToAirOrganicFertiliserIndirect.py +58 -66
- hestia_earth/models/ipcc2019/nh3ToAirOrganicFertiliser.py +104 -0
- hestia_earth/models/ipcc2019/noxToAirOrganicFertiliser.py +104 -0
- hestia_earth/models/ipcc2019/organicCarbonPerHa.py +60 -22
- hestia_earth/models/ipcc2019/utils.py +28 -16
- hestia_earth/models/mocking/search-results.json +35 -39
- hestia_earth/models/site/soilMeasurement.py +197 -0
- hestia_earth/models/utils/cycle.py +7 -6
- hestia_earth/models/utils/emission.py +15 -0
- hestia_earth/models/utils/site.py +1 -1
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.59.0.dist-info → hestia_earth_models-0.59.2.dist-info}/METADATA +1 -1
- {hestia_earth_models-0.59.0.dist-info → hestia_earth_models-0.59.2.dist-info}/RECORD +35 -25
- tests/models/ipcc2019/test_n2OToAirCropResidueDecompositionIndirect.py +71 -0
- tests/models/ipcc2019/test_n2OToAirExcretaIndirect.py +71 -0
- tests/models/ipcc2019/test_n2OToAirInorganicFertiliserIndirect.py +36 -13
- tests/models/ipcc2019/test_n2OToAirOrganicFertiliserIndirect.py +36 -13
- tests/models/ipcc2019/test_nh3ToAirOrganicFertiliser.py +35 -0
- tests/models/ipcc2019/test_noxToAirOrganicFertiliser.py +35 -0
- tests/models/ipcc2019/test_organicCarbonPerHa.py +45 -4
- tests/models/site/test_soilMeasurement.py +159 -0
- {hestia_earth_models-0.59.0.dist-info → hestia_earth_models-0.59.2.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.59.0.dist-info → hestia_earth_models-0.59.2.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.59.0.dist-info → hestia_earth_models-0.59.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from unittest.mock import patch
|
|
2
|
+
import json
|
|
3
|
+
from tests.utils import fixtures_path, fake_new_emission
|
|
4
|
+
|
|
5
|
+
from hestia_earth.models.ipcc2019.n2OToAirCropResidueDecompositionIndirect import (
|
|
6
|
+
MODEL, TERM_ID, run, _should_run, NH3_TERM_ID, NO3_TERM_ID, NOX_TERM_ID
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
class_path = f"hestia_earth.models.{MODEL}.{TERM_ID}"
|
|
10
|
+
fixtures_folder = f"{fixtures_path}/{MODEL}/{TERM_ID}"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_should_run():
|
|
14
|
+
# no emissions => no run
|
|
15
|
+
cycle = {'completeness': {'cropResidue': True}, 'emissions': []}
|
|
16
|
+
should_run, *args = _should_run(cycle)
|
|
17
|
+
assert not should_run
|
|
18
|
+
|
|
19
|
+
# with no3 emission => run
|
|
20
|
+
cycle['emissions'] = [
|
|
21
|
+
{
|
|
22
|
+
'term': {'@id': NO3_TERM_ID},
|
|
23
|
+
'value': [100]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
'term': {'@id': NH3_TERM_ID},
|
|
27
|
+
'value': [100]
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
'term': {'@id': NOX_TERM_ID},
|
|
31
|
+
'value': [100]
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
should_run, *args = _should_run(cycle)
|
|
35
|
+
assert should_run is True
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@patch(f"{class_path}._new_emission", side_effect=fake_new_emission)
|
|
39
|
+
def test_run(*args):
|
|
40
|
+
with open(f"{fixtures_folder}/cycle.jsonld", encoding='utf-8') as f:
|
|
41
|
+
cycle = json.load(f)
|
|
42
|
+
|
|
43
|
+
with open(f"{fixtures_folder}/result.jsonld", encoding='utf-8') as f:
|
|
44
|
+
expected = json.load(f)
|
|
45
|
+
|
|
46
|
+
value = run(cycle)
|
|
47
|
+
assert value == expected
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@patch(f"{class_path}._new_emission", side_effect=fake_new_emission)
|
|
51
|
+
def test_run_wet(*args):
|
|
52
|
+
with open(f"{fixtures_folder}/ecoClimateZone-wet/cycle.jsonld", encoding='utf-8') as f:
|
|
53
|
+
cycle = json.load(f)
|
|
54
|
+
|
|
55
|
+
with open(f"{fixtures_folder}/ecoClimateZone-wet/result.jsonld", encoding='utf-8') as f:
|
|
56
|
+
expected = json.load(f)
|
|
57
|
+
|
|
58
|
+
value = run(cycle)
|
|
59
|
+
assert value == expected
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@patch(f"{class_path}._new_emission", side_effect=fake_new_emission)
|
|
63
|
+
def test_run_dry(*args):
|
|
64
|
+
with open(f"{fixtures_folder}/ecoClimateZone-dry/cycle.jsonld", encoding='utf-8') as f:
|
|
65
|
+
cycle = json.load(f)
|
|
66
|
+
|
|
67
|
+
with open(f"{fixtures_folder}/ecoClimateZone-dry/result.jsonld", encoding='utf-8') as f:
|
|
68
|
+
expected = json.load(f)
|
|
69
|
+
|
|
70
|
+
value = run(cycle)
|
|
71
|
+
assert value == expected
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from unittest.mock import patch
|
|
2
|
+
import json
|
|
3
|
+
from tests.utils import fixtures_path, fake_new_emission
|
|
4
|
+
|
|
5
|
+
from hestia_earth.models.ipcc2019.n2OToAirExcretaIndirect import (
|
|
6
|
+
MODEL, TERM_ID, run, _should_run, NH3_TERM_ID, NO3_TERM_ID, NOX_TERM_ID
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
class_path = f"hestia_earth.models.{MODEL}.{TERM_ID}"
|
|
10
|
+
fixtures_folder = f"{fixtures_path}/{MODEL}/{TERM_ID}"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_should_run():
|
|
14
|
+
# no emissions => no run
|
|
15
|
+
cycle = {'completeness': {'excreta': True}, 'emissions': []}
|
|
16
|
+
should_run, *args = _should_run(cycle)
|
|
17
|
+
assert not should_run
|
|
18
|
+
|
|
19
|
+
# with no3 emission => run
|
|
20
|
+
cycle['emissions'] = [
|
|
21
|
+
{
|
|
22
|
+
'term': {'@id': NO3_TERM_ID},
|
|
23
|
+
'value': [100]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
'term': {'@id': NH3_TERM_ID},
|
|
27
|
+
'value': [100]
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
'term': {'@id': NOX_TERM_ID},
|
|
31
|
+
'value': [100]
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
should_run, *args = _should_run(cycle)
|
|
35
|
+
assert should_run is True
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@patch(f"{class_path}._new_emission", side_effect=fake_new_emission)
|
|
39
|
+
def test_run(*args):
|
|
40
|
+
with open(f"{fixtures_folder}/cycle.jsonld", encoding='utf-8') as f:
|
|
41
|
+
cycle = json.load(f)
|
|
42
|
+
|
|
43
|
+
with open(f"{fixtures_folder}/result.jsonld", encoding='utf-8') as f:
|
|
44
|
+
expected = json.load(f)
|
|
45
|
+
|
|
46
|
+
value = run(cycle)
|
|
47
|
+
assert value == expected
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@patch(f"{class_path}._new_emission", side_effect=fake_new_emission)
|
|
51
|
+
def test_run_wet(*args):
|
|
52
|
+
with open(f"{fixtures_folder}/ecoClimateZone-wet/cycle.jsonld", encoding='utf-8') as f:
|
|
53
|
+
cycle = json.load(f)
|
|
54
|
+
|
|
55
|
+
with open(f"{fixtures_folder}/ecoClimateZone-wet/result.jsonld", encoding='utf-8') as f:
|
|
56
|
+
expected = json.load(f)
|
|
57
|
+
|
|
58
|
+
value = run(cycle)
|
|
59
|
+
assert value == expected
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@patch(f"{class_path}._new_emission", side_effect=fake_new_emission)
|
|
63
|
+
def test_run_dry(*args):
|
|
64
|
+
with open(f"{fixtures_folder}/ecoClimateZone-dry/cycle.jsonld", encoding='utf-8') as f:
|
|
65
|
+
cycle = json.load(f)
|
|
66
|
+
|
|
67
|
+
with open(f"{fixtures_folder}/ecoClimateZone-dry/result.jsonld", encoding='utf-8') as f:
|
|
68
|
+
expected = json.load(f)
|
|
69
|
+
|
|
70
|
+
value = run(cycle)
|
|
71
|
+
assert value == expected
|
|
@@ -2,26 +2,49 @@ from unittest.mock import patch
|
|
|
2
2
|
import json
|
|
3
3
|
from tests.utils import fixtures_path, fake_new_emission
|
|
4
4
|
|
|
5
|
-
from hestia_earth.models.ipcc2019.n2OToAirInorganicFertiliserIndirect import
|
|
5
|
+
from hestia_earth.models.ipcc2019.n2OToAirInorganicFertiliserIndirect import (
|
|
6
|
+
MODEL, TERM_ID, run, _should_run, NH3_TERM_ID, NO3_TERM_ID, NOX_TERM_ID
|
|
7
|
+
)
|
|
6
8
|
|
|
7
9
|
class_path = f"hestia_earth.models.{MODEL}.{TERM_ID}"
|
|
8
10
|
fixtures_folder = f"{fixtures_path}/{MODEL}/{TERM_ID}"
|
|
9
11
|
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
assert not _should_run({})
|
|
13
|
+
def test_should_run():
|
|
14
|
+
# no emissions => no run
|
|
15
|
+
cycle = {'completeness': {'fertiliser': True}, 'emissions': []}
|
|
16
|
+
should_run, *args = _should_run(cycle)
|
|
17
|
+
assert not should_run
|
|
17
18
|
|
|
18
|
-
# with
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
# with no3 emission => run
|
|
20
|
+
cycle['emissions'] = [
|
|
21
|
+
{
|
|
22
|
+
'term': {'@id': NO3_TERM_ID},
|
|
23
|
+
'value': [100]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
'term': {'@id': NH3_TERM_ID},
|
|
27
|
+
'value': [100]
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
'term': {'@id': NOX_TERM_ID},
|
|
31
|
+
'value': [100]
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
should_run, *args = _should_run(cycle)
|
|
35
|
+
assert should_run is True
|
|
21
36
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
37
|
+
|
|
38
|
+
@patch(f"{class_path}._new_emission", side_effect=fake_new_emission)
|
|
39
|
+
def test_run(*args):
|
|
40
|
+
with open(f"{fixtures_folder}/cycle.jsonld", encoding='utf-8') as f:
|
|
41
|
+
cycle = json.load(f)
|
|
42
|
+
|
|
43
|
+
with open(f"{fixtures_folder}/result.jsonld", encoding='utf-8') as f:
|
|
44
|
+
expected = json.load(f)
|
|
45
|
+
|
|
46
|
+
value = run(cycle)
|
|
47
|
+
assert value == expected
|
|
25
48
|
|
|
26
49
|
|
|
27
50
|
@patch(f"{class_path}._new_emission", side_effect=fake_new_emission)
|
|
@@ -2,26 +2,49 @@ from unittest.mock import patch
|
|
|
2
2
|
import json
|
|
3
3
|
from tests.utils import fixtures_path, fake_new_emission
|
|
4
4
|
|
|
5
|
-
from hestia_earth.models.ipcc2019.n2OToAirOrganicFertiliserIndirect import
|
|
5
|
+
from hestia_earth.models.ipcc2019.n2OToAirOrganicFertiliserIndirect import (
|
|
6
|
+
MODEL, TERM_ID, run, _should_run, NH3_TERM_ID, NO3_TERM_ID, NOX_TERM_ID
|
|
7
|
+
)
|
|
6
8
|
|
|
7
9
|
class_path = f"hestia_earth.models.{MODEL}.{TERM_ID}"
|
|
8
10
|
fixtures_folder = f"{fixtures_path}/{MODEL}/{TERM_ID}"
|
|
9
11
|
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
assert not _should_run({})
|
|
13
|
+
def test_should_run():
|
|
14
|
+
# no emissions => no run
|
|
15
|
+
cycle = {'completeness': {'fertiliser': True}, 'emissions': []}
|
|
16
|
+
should_run, *args = _should_run(cycle)
|
|
17
|
+
assert not should_run
|
|
17
18
|
|
|
18
|
-
# with
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
# with no3 emission => run
|
|
20
|
+
cycle['emissions'] = [
|
|
21
|
+
{
|
|
22
|
+
'term': {'@id': NO3_TERM_ID},
|
|
23
|
+
'value': [100]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
'term': {'@id': NH3_TERM_ID},
|
|
27
|
+
'value': [100]
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
'term': {'@id': NOX_TERM_ID},
|
|
31
|
+
'value': [100]
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
should_run, *args = _should_run(cycle)
|
|
35
|
+
assert should_run is True
|
|
21
36
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
37
|
+
|
|
38
|
+
@patch(f"{class_path}._new_emission", side_effect=fake_new_emission)
|
|
39
|
+
def test_run(*args):
|
|
40
|
+
with open(f"{fixtures_folder}/cycle.jsonld", encoding='utf-8') as f:
|
|
41
|
+
cycle = json.load(f)
|
|
42
|
+
|
|
43
|
+
with open(f"{fixtures_folder}/result.jsonld", encoding='utf-8') as f:
|
|
44
|
+
expected = json.load(f)
|
|
45
|
+
|
|
46
|
+
value = run(cycle)
|
|
47
|
+
assert value == expected
|
|
25
48
|
|
|
26
49
|
|
|
27
50
|
@patch(f"{class_path}._new_emission", side_effect=fake_new_emission)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from unittest.mock import patch
|
|
2
|
+
import json
|
|
3
|
+
from tests.utils import fixtures_path, fake_new_emission
|
|
4
|
+
|
|
5
|
+
from hestia_earth.models.ipcc2019.nh3ToAirOrganicFertiliser import MODEL, TERM_ID, run, _should_run
|
|
6
|
+
|
|
7
|
+
class_path = f"hestia_earth.models.{MODEL}.{TERM_ID}"
|
|
8
|
+
fixtures_folder = f"{fixtures_path}/{MODEL}/{TERM_ID}"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@patch(f"{class_path}._is_term_type_complete", return_value=False)
|
|
12
|
+
@patch(f"{class_path}.get_organic_fertiliser_N_total", return_value=0)
|
|
13
|
+
def test_should_run(mock_N_total, mock_complete, *args):
|
|
14
|
+
# no N => no run
|
|
15
|
+
assert not _should_run({})
|
|
16
|
+
|
|
17
|
+
# with N => no run
|
|
18
|
+
mock_N_total.return_value = 10
|
|
19
|
+
assert not _should_run({})
|
|
20
|
+
|
|
21
|
+
# is complete => run
|
|
22
|
+
mock_complete.return_value = True
|
|
23
|
+
assert _should_run({}) is True
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@patch(f"{class_path}._new_emission", side_effect=fake_new_emission)
|
|
27
|
+
def test_run(*args):
|
|
28
|
+
with open(f"{fixtures_folder}/cycle.jsonld", encoding='utf-8') as f:
|
|
29
|
+
cycle = json.load(f)
|
|
30
|
+
|
|
31
|
+
with open(f"{fixtures_folder}/result.jsonld", encoding='utf-8') as f:
|
|
32
|
+
expected = json.load(f)
|
|
33
|
+
|
|
34
|
+
value = run(cycle)
|
|
35
|
+
assert value == expected
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from unittest.mock import patch
|
|
2
|
+
import json
|
|
3
|
+
from tests.utils import fixtures_path, fake_new_emission
|
|
4
|
+
|
|
5
|
+
from hestia_earth.models.ipcc2019.noxToAirOrganicFertiliser import MODEL, TERM_ID, run, _should_run
|
|
6
|
+
|
|
7
|
+
class_path = f"hestia_earth.models.{MODEL}.{TERM_ID}"
|
|
8
|
+
fixtures_folder = f"{fixtures_path}/{MODEL}/{TERM_ID}"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@patch(f"{class_path}._is_term_type_complete", return_value=False)
|
|
12
|
+
@patch(f"{class_path}.get_organic_fertiliser_N_total", return_value=0)
|
|
13
|
+
def test_should_run(mock_N_total, mock_complete, *args):
|
|
14
|
+
# no N => no run
|
|
15
|
+
assert not _should_run({})
|
|
16
|
+
|
|
17
|
+
# with N => no run
|
|
18
|
+
mock_N_total.return_value = 10
|
|
19
|
+
assert not _should_run({})
|
|
20
|
+
|
|
21
|
+
# is complete => run
|
|
22
|
+
mock_complete.return_value = True
|
|
23
|
+
assert _should_run({}) is True
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@patch(f"{class_path}._new_emission", side_effect=fake_new_emission)
|
|
27
|
+
def test_run(*args):
|
|
28
|
+
with open(f"{fixtures_folder}/cycle.jsonld", encoding='utf-8') as f:
|
|
29
|
+
cycle = json.load(f)
|
|
30
|
+
|
|
31
|
+
with open(f"{fixtures_folder}/result.jsonld", encoding='utf-8') as f:
|
|
32
|
+
expected = json.load(f)
|
|
33
|
+
|
|
34
|
+
value = run(cycle)
|
|
35
|
+
assert value == expected
|
|
@@ -16,6 +16,7 @@ from hestia_earth.models.ipcc2019.organicCarbonPerHa import (
|
|
|
16
16
|
_calc_temperature_factor,
|
|
17
17
|
_calc_tier_1_soc_stocks,
|
|
18
18
|
_calc_water_factor,
|
|
19
|
+
_calculated_cycles,
|
|
19
20
|
_check_cropland_low_category,
|
|
20
21
|
_check_cropland_medium_category,
|
|
21
22
|
_get_carbon_input_kwargs,
|
|
@@ -85,6 +86,25 @@ UPLAND_RICE_CROP_TERM_IDS = [
|
|
|
85
86
|
"riceGrainInHuskUpland"
|
|
86
87
|
]
|
|
87
88
|
|
|
89
|
+
DEFAULT_PROPERTIES = {
|
|
90
|
+
"manureDryKgMass": {
|
|
91
|
+
"carbonContent": {
|
|
92
|
+
"value": 38.4
|
|
93
|
+
},
|
|
94
|
+
"nitrogenContent": {
|
|
95
|
+
"value": 2.65
|
|
96
|
+
},
|
|
97
|
+
"ligninContent": {
|
|
98
|
+
"value": 9.67
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def find_term_property_side_effect(term: dict, property: str, *_):
|
|
105
|
+
term_id = term.get('@id', None)
|
|
106
|
+
return DEFAULT_PROPERTIES.get(term_id, {}).get(property, {})
|
|
107
|
+
|
|
88
108
|
|
|
89
109
|
# --- TIER 1 & TIER 2 TESTS ---
|
|
90
110
|
|
|
@@ -96,11 +116,13 @@ RUN_SUBFOLDERS = [
|
|
|
96
116
|
("tier-2/with-incomplete-climate-data", True), # Closes issue 599
|
|
97
117
|
("tier-2/with-initial-soc", True),
|
|
98
118
|
("tier-2/with-multi-year-cycles", True),
|
|
119
|
+
("tier-2/with-multi-year-cycles-and-missing-properties", True), # Closes issue 734
|
|
99
120
|
("tier-2/without-any-measurements", True), # Closes issue 594
|
|
100
121
|
("tier-2/without-initial-soc", True),
|
|
101
122
|
("tier-2/with-irrigation", True),
|
|
102
123
|
("tier-2/with-irrigation-dates", True),
|
|
103
124
|
("tier-2/with-paddy-rice", True),
|
|
125
|
+
("tier-2/with-sand-without-date", True), # Closes issue 739
|
|
104
126
|
("tier-2/with-irrigated-upland-rice", True),
|
|
105
127
|
("tier-1/cropland-depth-as-float", False),
|
|
106
128
|
("tier-1/cropland-with-measured-soc", False),
|
|
@@ -124,9 +146,11 @@ RUN_SUBFOLDERS = [
|
|
|
124
146
|
@patch(f"{class_path}.get_residue_removed_or_burnt_terms", return_value=RESIDUE_REMOVED_OR_BURNT_TERM_IDS)
|
|
125
147
|
@patch(f"{class_path}.get_upland_rice_land_cover_terms", return_value=UPLAND_RICE_LAND_COVER_TERM_IDS)
|
|
126
148
|
@patch(f"{class_path}.get_upland_rice_crop_terms", return_value=UPLAND_RICE_CROP_TERM_IDS)
|
|
127
|
-
@patch(f"{class_path}.
|
|
149
|
+
@patch(f"{class_path}._calculated_cycles")
|
|
150
|
+
@patch("hestia_earth.models.utils.property.find_term_property")
|
|
128
151
|
def test_run(
|
|
129
|
-
|
|
152
|
+
mock_find_term_property,
|
|
153
|
+
mock_calculated_cycles,
|
|
130
154
|
_mock_get_upland_rice_crop_terms,
|
|
131
155
|
_mock_get_upland_rice_land_cover_terms,
|
|
132
156
|
_mock_get_residue_removed_or_burnt_terms,
|
|
@@ -144,7 +168,8 @@ def test_run(
|
|
|
144
168
|
with open(f"{folder}/cycles.jsonld", encoding='utf-8') as f:
|
|
145
169
|
return json.load(f)
|
|
146
170
|
|
|
147
|
-
|
|
171
|
+
mock_find_term_property.side_effect = find_term_property_side_effect
|
|
172
|
+
mock_calculated_cycles.return_value = load_cycles_from_file() if load_cycles else []
|
|
148
173
|
|
|
149
174
|
with open(f"{folder}/site.jsonld", encoding='utf-8') as f:
|
|
150
175
|
site = json.load(f)
|
|
@@ -164,7 +189,7 @@ def test_run(
|
|
|
164
189
|
@patch(f"{class_path}.get_residue_removed_or_burnt_terms", return_value=RESIDUE_REMOVED_OR_BURNT_TERM_IDS)
|
|
165
190
|
@patch(f"{class_path}.get_upland_rice_land_cover_terms", return_value=UPLAND_RICE_LAND_COVER_TERM_IDS)
|
|
166
191
|
@patch(f"{class_path}.get_upland_rice_crop_terms", return_value=UPLAND_RICE_CROP_TERM_IDS)
|
|
167
|
-
@patch(f"{class_path}.
|
|
192
|
+
@patch(f"{class_path}._calculated_cycles", return_value=[])
|
|
168
193
|
def test_run_no_data(*args):
|
|
169
194
|
SITE = {}
|
|
170
195
|
EXPECTED = []
|
|
@@ -209,6 +234,22 @@ def test_calc_water_factor():
|
|
|
209
234
|
assert _calc_water_factor(1, 1) == _calc_water_factor(1000, 1000)
|
|
210
235
|
|
|
211
236
|
|
|
237
|
+
@patch(f"{class_path}.find_related")
|
|
238
|
+
@patch("hestia_earth.models.utils.download_hestia")
|
|
239
|
+
def test_calculated_cycles(mock_download_hestia, mock_find_related):
|
|
240
|
+
CYCLES = [{"@id": "cycle"}]
|
|
241
|
+
EXPECTED = [{"@id": "cycle", "@type": "Cycle"}]
|
|
242
|
+
|
|
243
|
+
mock_download_hestia.side_effect = lambda node_id, node_type, **_: {
|
|
244
|
+
"@type": node_type.value,
|
|
245
|
+
"@id": node_id
|
|
246
|
+
}
|
|
247
|
+
mock_find_related.return_value = CYCLES
|
|
248
|
+
|
|
249
|
+
result = _calculated_cycles({})
|
|
250
|
+
assert EXPECTED == result
|
|
251
|
+
|
|
252
|
+
|
|
212
253
|
# --- IPCC SOIL CATEGORY TESTS ---
|
|
213
254
|
|
|
214
255
|
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
from unittest.mock import patch
|
|
2
|
+
import json
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from tests.utils import fixtures_path, fake_new_measurement
|
|
6
|
+
|
|
7
|
+
from hestia_earth.models.site.soilMeasurement import (
|
|
8
|
+
MODEL,
|
|
9
|
+
MODEL_KEY,
|
|
10
|
+
_get_overlap,
|
|
11
|
+
_harmonise_measurements,
|
|
12
|
+
_should_run, run
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
class_path = f"hestia_earth.models.{MODEL}.{MODEL_KEY}"
|
|
16
|
+
fixtures_folder = f"{fixtures_path}/site/soilMeasurement"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@pytest.mark.parametrize(
|
|
20
|
+
"data,expected",
|
|
21
|
+
[
|
|
22
|
+
((1, 10, 2, 150), 8),
|
|
23
|
+
((1, 10, 5, 15), 5),
|
|
24
|
+
((1, 10, 10, 15), 0),
|
|
25
|
+
((10, 10, 0, 150), 0),
|
|
26
|
+
((150, 155, 1, 150), 0),
|
|
27
|
+
((20, 40, 0, 30), 10),
|
|
28
|
+
((0, 20, 0, 50), 20),
|
|
29
|
+
((20, 60, 0, 50), 30),
|
|
30
|
+
((10, 20, 40, 50), 0),
|
|
31
|
+
((10, 20, 19, 50), 1),
|
|
32
|
+
]
|
|
33
|
+
)
|
|
34
|
+
def test_get_overlap(data, expected):
|
|
35
|
+
result = _get_overlap(
|
|
36
|
+
data[0], data[1], data[2], data[3]
|
|
37
|
+
)
|
|
38
|
+
assert result == expected
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@pytest.mark.parametrize(
|
|
42
|
+
"measurements_list,returns_dict,expected_value",
|
|
43
|
+
[
|
|
44
|
+
(
|
|
45
|
+
[
|
|
46
|
+
{"value": [7.5], "depthUpper": 0, "depthLower": 20},
|
|
47
|
+
{"value": [6], "depthUpper": 20, "depthLower": 40},
|
|
48
|
+
],
|
|
49
|
+
{"depthUpper": 0, "depthLower": 30},
|
|
50
|
+
7
|
|
51
|
+
),
|
|
52
|
+
(
|
|
53
|
+
[
|
|
54
|
+
{"value": [7.5], "depthUpper": 0, "depthLower": 20},
|
|
55
|
+
{"value": [6], "depthUpper": 20, "depthLower": 40},
|
|
56
|
+
],
|
|
57
|
+
{"depthUpper": 0, "depthLower": 50},
|
|
58
|
+
6.75
|
|
59
|
+
),
|
|
60
|
+
]
|
|
61
|
+
)
|
|
62
|
+
def test_harmonise_measurements(measurements_list, returns_dict, expected_value):
|
|
63
|
+
actual_value = _harmonise_measurements(
|
|
64
|
+
measurements_list=measurements_list,
|
|
65
|
+
standard_depth_upper=returns_dict["depthUpper"],
|
|
66
|
+
standard_depth_lower=returns_dict["depthLower"],
|
|
67
|
+
)
|
|
68
|
+
assert actual_value == expected_value
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@pytest.mark.parametrize(
|
|
72
|
+
"test_name,site,expected_should_run",
|
|
73
|
+
[
|
|
74
|
+
(
|
|
75
|
+
"no measurement => no run",
|
|
76
|
+
{"measurements": []},
|
|
77
|
+
False
|
|
78
|
+
),
|
|
79
|
+
(
|
|
80
|
+
"missing dates => run",
|
|
81
|
+
{
|
|
82
|
+
"measurements":
|
|
83
|
+
[
|
|
84
|
+
{
|
|
85
|
+
"term": {"@id": "clayContent"},
|
|
86
|
+
"depthUpper": 0,
|
|
87
|
+
"depthLower": 20
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
},
|
|
91
|
+
True
|
|
92
|
+
),
|
|
93
|
+
(
|
|
94
|
+
"no depthUpper => no run",
|
|
95
|
+
{
|
|
96
|
+
"measurements":
|
|
97
|
+
[
|
|
98
|
+
{
|
|
99
|
+
"term": {"@id": "clayContent"},
|
|
100
|
+
"dates": ["2022-01-02"],
|
|
101
|
+
"depthLower": 20
|
|
102
|
+
}
|
|
103
|
+
]
|
|
104
|
+
},
|
|
105
|
+
False
|
|
106
|
+
),
|
|
107
|
+
(
|
|
108
|
+
"all fields => run",
|
|
109
|
+
{
|
|
110
|
+
"measurements":
|
|
111
|
+
[
|
|
112
|
+
{
|
|
113
|
+
"term": {"@id": "clayContent"},
|
|
114
|
+
"dates": ["2022-01-02"],
|
|
115
|
+
"depthUpper": 0,
|
|
116
|
+
"depthLower": 20
|
|
117
|
+
}
|
|
118
|
+
]
|
|
119
|
+
},
|
|
120
|
+
True
|
|
121
|
+
)
|
|
122
|
+
]
|
|
123
|
+
)
|
|
124
|
+
@patch(f"{class_path}.get_lookup_value")
|
|
125
|
+
def test_should_run(mock_get_lookup, test_name, site, expected_should_run):
|
|
126
|
+
mock_get_lookup.return_value = True
|
|
127
|
+
model_key = "clayContent"
|
|
128
|
+
should_run, *args = _should_run(site=site, model_key=model_key)
|
|
129
|
+
assert should_run == expected_should_run, test_name
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def lookup_side_effect(*args, **kwargs):
|
|
133
|
+
_ = kwargs
|
|
134
|
+
if args[0]["@id"] == "soilPh" and args[1] == "depthSensitive":
|
|
135
|
+
return False
|
|
136
|
+
elif args[0]["@id"] in {"baseSaturation", "soilDepth", "rainfallHourly"}:
|
|
137
|
+
return False
|
|
138
|
+
return True
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@pytest.mark.parametrize(
|
|
142
|
+
"test_name",
|
|
143
|
+
[
|
|
144
|
+
"missingDepth", "simpleSoilPh", "clayContent", "missingDepth", "nonUniqueMeasurements", "arrays"
|
|
145
|
+
]
|
|
146
|
+
)
|
|
147
|
+
@patch(f"{class_path}._new_measurement", side_effect=fake_new_measurement)
|
|
148
|
+
@patch(f"{class_path}.get_lookup_value")
|
|
149
|
+
def test_run(mock_lookup, mock_new_measurement, test_name):
|
|
150
|
+
mock_lookup.side_effect = lookup_side_effect
|
|
151
|
+
|
|
152
|
+
with open(f"{fixtures_folder}/{test_name}/site.jsonld", encoding='utf-8') as f:
|
|
153
|
+
site = json.load(f)
|
|
154
|
+
|
|
155
|
+
with open(f"{fixtures_folder}/{test_name}/result.jsonld", encoding='utf-8') as f:
|
|
156
|
+
result = json.load(f)
|
|
157
|
+
|
|
158
|
+
value = run(site)
|
|
159
|
+
assert value == result
|
|
File without changes
|
|
File without changes
|
|
File without changes
|