hestia-earth-models 0.64.14__py3-none-any.whl → 0.65.0__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.

Files changed (112) hide show
  1. hestia_earth/models/agribalyse2016/fuelElectricity.py +1 -1
  2. hestia_earth/models/cache_sites.py +15 -24
  3. hestia_earth/models/chaudharyBrooks2018/damageToTerrestrialEcosystemsLandTransformation.py +6 -9
  4. hestia_earth/models/cycle/input/hestiaAggregatedData.py +46 -22
  5. hestia_earth/models/cycle/pre_checks/cache_sources.py +3 -25
  6. hestia_earth/models/cycle/product/economicValueShare.py +2 -2
  7. hestia_earth/models/environmentalFootprintV3/soilQualityIndexLandTransformation.py +11 -33
  8. hestia_earth/models/faostat2018/landTransformation100YearAverageDuringCycle.py +34 -0
  9. hestia_earth/models/faostat2018/landTransformation20YearAverageDuringCycle.py +34 -0
  10. hestia_earth/models/faostat2018/utils.py +47 -3
  11. hestia_earth/models/hestia/landCover.py +5 -5
  12. hestia_earth/models/hestia/seed_emissions.py +275 -0
  13. hestia_earth/models/ipcc2019/aboveGroundBiomass.py +2 -2
  14. hestia_earth/models/ipcc2019/belowGroundBiomass.py +8 -2
  15. hestia_earth/models/ipcc2019/biomass_utils.py +11 -4
  16. hestia_earth/models/ipcc2019/ch4ToAirEntericFermentation.py +19 -10
  17. hestia_earth/models/ipcc2019/co2ToAirAboveGroundBiomassStockChange.py +2 -1
  18. hestia_earth/models/ipcc2019/co2ToAirBelowGroundBiomassStockChange.py +2 -1
  19. hestia_earth/models/ipcc2019/co2ToAirCarbonStockChange_utils.py +8 -7
  20. hestia_earth/models/ipcc2019/co2ToAirSoilOrganicCarbonStockChange.py +2 -1
  21. hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_1_utils.py +28 -34
  22. hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_2_utils.py +8 -12
  23. hestia_earth/models/ipcc2019/organicCarbonPerHa_utils.py +13 -30
  24. hestia_earth/models/linkedImpactAssessment/{landTransformationFromCropland20YearAverageInputsProduction.py → landTransformation100YearAverageInputsProduction.py} +5 -2
  25. hestia_earth/models/linkedImpactAssessment/{landTransformationFromCropland100YearAverageInputsProduction.py → landTransformation20YearAverageInputsProduction.py} +5 -2
  26. hestia_earth/models/linkedImpactAssessment/utils.py +69 -12
  27. hestia_earth/models/mocking/search-results.json +444 -444
  28. hestia_earth/models/pooreNemecek2018/excretaKgN.py +45 -41
  29. hestia_earth/models/pooreNemecek2018/excretaKgVs.py +89 -63
  30. hestia_earth/models/pooreNemecek2018/saplingsDepreciatedAmountPerCycle.py +8 -8
  31. hestia_earth/models/pooreNemecek2018/utils.py +60 -19
  32. hestia_earth/models/schererPfister2015/nErosionSoilFlux.py +4 -3
  33. hestia_earth/models/schererPfister2015/pErosionSoilFlux.py +4 -3
  34. hestia_earth/models/schererPfister2015/utils.py +12 -9
  35. hestia_earth/models/site/management.py +70 -55
  36. hestia_earth/models/site/pre_checks/cache_sources.py +2 -20
  37. hestia_earth/models/utils/__init__.py +12 -1
  38. hestia_earth/models/utils/aggregated.py +1 -1
  39. hestia_earth/models/utils/blank_node.py +20 -12
  40. hestia_earth/models/utils/cache_sources.py +15 -0
  41. hestia_earth/models/utils/crop.py +5 -0
  42. hestia_earth/models/utils/indicator.py +3 -1
  43. hestia_earth/models/version.py +1 -1
  44. {hestia_earth_models-0.64.14.dist-info → hestia_earth_models-0.65.0.dist-info}/METADATA +2 -2
  45. {hestia_earth_models-0.64.14.dist-info → hestia_earth_models-0.65.0.dist-info}/RECORD +75 -104
  46. tests/models/cml2001Baseline/test_abioticResourceDepletionMineralsAndMetals.py +1 -1
  47. tests/models/cycle/input/test_hestiaAggregatedData.py +5 -2
  48. tests/models/environmentalFootprintV3/test_soilQualityIndexLandTransformation.py +39 -28
  49. tests/models/{hyde32/test_landTransformationFromForest20YearAverageDuringCycle.py → faostat2018/test_landTransformation100YearAverageDuringCycle.py} +5 -5
  50. tests/models/{hyde32/test_landTransformationFromForest100YearAverageDuringCycle.py → faostat2018/test_landTransformation20YearAverageDuringCycle.py} +5 -5
  51. tests/models/faostat2018/test_utils.py +28 -0
  52. tests/models/hestia/test_landCover.py +2 -1
  53. tests/models/hestia/test_seed_emissions.py +27 -0
  54. tests/models/ipcc2019/test_aboveGroundBiomass.py +40 -4
  55. tests/models/ipcc2019/test_belowGroundBiomass.py +40 -4
  56. tests/models/ipcc2019/test_co2ToAirAboveGroundBiomassStockChange.py +52 -15
  57. tests/models/ipcc2019/test_co2ToAirBelowGroundBiomassStockChange.py +50 -14
  58. tests/models/ipcc2019/test_co2ToAirSoilOrganicCarbonStockChange.py +53 -32
  59. tests/models/ipcc2019/test_organicCarbonPerHa.py +91 -108
  60. tests/models/ipcc2019/test_organicCarbonPerHa_tier_1_utils.py +33 -50
  61. tests/models/ipcc2019/test_organicCarbonPerHa_tier_2_utils.py +0 -52
  62. tests/models/linkedImpactAssessment/test_freshwaterWithdrawalsInputsProduction.py +6 -4
  63. tests/models/linkedImpactAssessment/test_landOccupationInputsProduction.py +6 -4
  64. tests/models/linkedImpactAssessment/{test_landTransformationFromForest100YearAverageInputsProduction.py → test_landTransformation100YearAverageInputsProduction.py} +7 -5
  65. tests/models/linkedImpactAssessment/{test_landTransformationFromForest20YearAverageInputsProduction.py → test_landTransformation20YearAverageInputsProduction.py} +7 -5
  66. tests/models/pooreNemecek2018/test_excretaKgN.py +2 -2
  67. tests/models/pooreNemecek2018/test_excretaKgVs.py +1 -1
  68. tests/models/pooreNemecek2018/test_utils.py +26 -0
  69. tests/models/site/test_management.py +10 -27
  70. tests/models/test_cache_sites.py +40 -12
  71. tests/models/utils/test_blank_node.py +0 -8
  72. tests/models/utils/test_cache_sources.py +21 -0
  73. hestia_earth/models/blonkConsultants2016/landTransformationFromForest20YearAverageDuringCycle.py +0 -90
  74. hestia_earth/models/faostat2018/landTransformationFromCropland100YearAverage.py +0 -74
  75. hestia_earth/models/faostat2018/landTransformationFromCropland20YearAverage.py +0 -74
  76. hestia_earth/models/hyde32/__init__.py +0 -13
  77. hestia_earth/models/hyde32/landTransformationFromCropland100YearAverageDuringCycle.py +0 -60
  78. hestia_earth/models/hyde32/landTransformationFromCropland20YearAverageDuringCycle.py +0 -60
  79. hestia_earth/models/hyde32/landTransformationFromForest100YearAverageDuringCycle.py +0 -60
  80. hestia_earth/models/hyde32/landTransformationFromForest20YearAverageDuringCycle.py +0 -60
  81. hestia_earth/models/hyde32/landTransformationFromOtherNaturalVegetation100YearAverageDuringCycle.py +0 -61
  82. hestia_earth/models/hyde32/landTransformationFromOtherNaturalVegetation20YearAverageDuringCycle.py +0 -61
  83. hestia_earth/models/hyde32/landTransformationFromPermanentPasture100YearAverageDuringCycle.py +0 -61
  84. hestia_earth/models/hyde32/landTransformationFromPermanentPasture20YearAverageDuringCycle.py +0 -61
  85. hestia_earth/models/hyde32/utils.py +0 -72
  86. hestia_earth/models/linkedImpactAssessment/landTransformationFromForest100YearAverageInputsProduction.py +0 -36
  87. hestia_earth/models/linkedImpactAssessment/landTransformationFromForest20YearAverageInputsProduction.py +0 -36
  88. hestia_earth/models/linkedImpactAssessment/landTransformationFromOtherNaturalVegetation100YearAverageInputsProduction.py +0 -36
  89. hestia_earth/models/linkedImpactAssessment/landTransformationFromOtherNaturalVegetation20YearAverageInputsProduction.py +0 -36
  90. hestia_earth/models/linkedImpactAssessment/landTransformationFromPermanentPasture100YearAverageInputsProduction.py +0 -36
  91. hestia_earth/models/linkedImpactAssessment/landTransformationFromPermanentPasture20YearAverageInputsProduction.py +0 -36
  92. tests/models/blonkConsultants2016/test_landTransformationFromForest20YearAverageDuringCycle.py +0 -36
  93. tests/models/cycle/pre_checks/test_cache_sources.py +0 -25
  94. tests/models/faostat2018/test_landTransformationFromCropland100YearAverage.py +0 -40
  95. tests/models/faostat2018/test_landTransformationFromCropland20YearAverage.py +0 -40
  96. tests/models/hyde32/__init__.py +0 -0
  97. tests/models/hyde32/test_landTransformationFromCropland100YearAverageDuringCycle.py +0 -21
  98. tests/models/hyde32/test_landTransformationFromCropland20YearAverageDuringCycle.py +0 -21
  99. tests/models/hyde32/test_landTransformationFromOtherNaturalVegetation100YearAverageDuringCycle.py +0 -23
  100. tests/models/hyde32/test_landTransformationFromOtherNaturalVegetation20YearAverageDuringCycle.py +0 -21
  101. tests/models/hyde32/test_landTransformationFromPermanentPasture100YearAverageDuringCycle.py +0 -21
  102. tests/models/hyde32/test_landTransformationFromPermanentPasture20YearAverageDuringCycle.py +0 -21
  103. tests/models/linkedImpactAssessment/test_landTransformationFromCropland100YearAverageInputsProduction.py +0 -23
  104. tests/models/linkedImpactAssessment/test_landTransformationFromCropland20YearAverageInputsProduction.py +0 -23
  105. tests/models/linkedImpactAssessment/test_landTransformationFromOtherNaturalVegetation100YearAverageInputsProduction.py +0 -23
  106. tests/models/linkedImpactAssessment/test_landTransformationFromOtherNaturalVegetation20YearAverageInputsProduction.py +0 -23
  107. tests/models/linkedImpactAssessment/test_landTransformationFromPermanentPasture100YearAverageInputsProduction.py +0 -24
  108. tests/models/linkedImpactAssessment/test_landTransformationFromPermanentPasture20YearAverageInputsProduction.py +0 -24
  109. tests/models/site/pre_checks/test_cache_sources.py +0 -21
  110. {hestia_earth_models-0.64.14.dist-info → hestia_earth_models-0.65.0.dist-info}/LICENSE +0 -0
  111. {hestia_earth_models-0.64.14.dist-info → hestia_earth_models-0.65.0.dist-info}/WHEEL +0 -0
  112. {hestia_earth_models-0.64.14.dist-info → hestia_earth_models-0.65.0.dist-info}/top_level.txt +0 -0
@@ -1,10 +1,11 @@
1
1
  import json
2
- from unittest.mock import patch
2
+ from unittest.mock import Mock, patch
3
3
 
4
4
  from pytest import mark
5
5
 
6
- from hestia_earth.models.environmentalFootprintV3.soilQualityIndexLandTransformation import MODEL, TERM_ID, run, \
7
- _should_run
6
+ from hestia_earth.models.environmentalFootprintV3.soilQualityIndexLandTransformation import (
7
+ MODEL, TERM_ID, run, _should_run
8
+ )
8
9
  from tests.utils import fixtures_path, fake_new_indicator
9
10
 
10
11
  class_path = f"hestia_earth.models.{MODEL}.{TERM_ID}"
@@ -17,55 +18,70 @@ def fake_rounded_indicator(value: float):
17
18
  return indicator
18
19
 
19
20
 
21
+ land_cover_terms = ['cropland', 'seaOrOcean', 'forest']
20
22
  crop_land = {"@id": "cropland", "termType": "landCover"}
21
23
  sea_land_cover = {"@id": "seaOrOcean", "termType": "landCover"}
22
24
  forest = {"@id": "forest", "termType": "landCover"}
25
+ indicator = {
26
+ "@id": "landTransformation20YearAverageInputsProduction",
27
+ "termType": "resourceUse",
28
+ "units": "m2 / year"
29
+ }
23
30
 
24
31
  wrong_indicator = {"term": {"@id": "NOT_VALID_INDICATOR_ID", "termType": "resourceUse", "units": "m2 / year"},
25
32
  "value": 0.5, "landCover": crop_land}
26
33
 
27
34
  indicator_no_land_cover = {
28
- "term": {"@id": "landTransformationFromForest20YearAverageInputsProduction", "termType": "resourceUse",
29
- "units": "m2 / year"},
35
+ "term": indicator,
36
+ "previousLandCover": forest,
30
37
  "value": 0.5}
31
38
 
32
39
  indicator_no_unit = {
33
- "term": {"@id": "landTransformationFromForest20YearAverageInputsProduction", "termType": "resourceUse"},
34
- "value": 0.5, "landCover": crop_land}
40
+ "term": indicator,
41
+ "previousLandCover": forest,
42
+ "value": 0.5,
43
+ "landCover": crop_land}
35
44
 
36
45
  indicator_wrong_unit = {
37
- "term": {"@id": "landTransformationFromForest20YearAverageInputsProduction", "termType": "resourceUse",
38
- "units": "ha*day"}, "value": 0.5,
46
+ "term": indicator,
47
+ "value": 0.5,
48
+ "previousLandCover": forest,
39
49
  "landCover": crop_land}
40
50
 
41
51
  indicator_bad_area_value = {
42
- "term": {"@id": "landTransformationFromForest20YearAverageInputsProduction", "termType": "resourceUse",
43
- "units": "m2 / year"}, "value": -10,
52
+ "term": indicator,
53
+ "value": -10,
54
+ "previousLandCover": forest,
44
55
  "landCover": crop_land}
45
56
 
46
57
  inputs_production_indicator_from_forest_to_no_cf = {
47
- "term": {"@id": "landTransformationFromForest20YearAverageInputsProduction", "termType": "resourceUse",
48
- "units": "m2 / year"}, "value": 0.5,
58
+ "term": indicator,
59
+ "value": 0.5,
60
+ "previousLandCover": forest,
49
61
  "landCover": sea_land_cover}
50
62
 
51
63
  good_inputs_production_indicator_from_forest_to_cropland = {
52
- "term": {"@id": "landTransformationFromForest20YearAverageInputsProduction", "termType": "resourceUse",
53
- "units": "m2 / year"}, "value": 0.5,
64
+ "term": indicator,
65
+ "value": 0.5,
66
+ "previousLandCover": forest,
54
67
  "landCover": crop_land}
55
68
 
56
69
  good_inputs_production_indicator_from_forest_to_forest = {
57
- "term": {"@id": "landTransformationFromForest20YearAverageInputsProduction", "termType": "resourceUse",
58
- "units": "m2 / year"}, "value": 0.5,
70
+ "term": indicator,
71
+ "value": 0.5,
72
+ "previousLandCover": forest,
59
73
  "landCover": forest}
60
74
 
61
75
  good_during_cycle_indicator_from_forest_to_cropland = {
62
- "term": {"@id": "landTransformationFromForest20YearAverageDuringCycle", "termType": "resourceUse",
63
- "units": "m2 / year"}, "value": 0.5,
76
+ "term": indicator,
77
+ "value": 0.5,
78
+ "previousLandCover": forest,
64
79
  "landCover": crop_land}
65
80
 
66
81
  good_during_cycle_indicator_from_forest_to_forest = {
67
- "term": {"@id": "landTransformationFromForest20YearAverageDuringCycle", "termType": "resourceUse",
68
- "units": "m2 / year"}, "value": 0.5,
82
+ "term": indicator,
83
+ "value": 0.5,
84
+ "previousLandCover": forest,
69
85
  "landCover": forest}
70
86
 
71
87
 
@@ -75,8 +91,6 @@ good_during_cycle_indicator_from_forest_to_forest = {
75
91
  ([], True, 0),
76
92
  ([wrong_indicator], True, 0),
77
93
  ([indicator_no_land_cover], False, 0),
78
- ([indicator_no_unit], False, 0),
79
- ([indicator_wrong_unit], False, 0),
80
94
  ([indicator_bad_area_value], False, 0),
81
95
  ([good_during_cycle_indicator_from_forest_to_cropland], True, 1),
82
96
  ([good_during_cycle_indicator_from_forest_to_forest], True, 1),
@@ -89,8 +103,6 @@ good_during_cycle_indicator_from_forest_to_forest = {
89
103
  ids=["No emissionsResourceUse => run, 0 dict",
90
104
  "Wrong indicator termid => run, 0 dict",
91
105
  "Indicator no landcover terms => no run",
92
- "Missing unit => no run",
93
- "Wrong unit => no run",
94
106
  "Bad m2 / year value => no run",
95
107
  "One good during cycle transformation => run, 1 dict",
96
108
  "One 0 during cycle transformation => run, 1 dict",
@@ -100,8 +112,7 @@ good_during_cycle_indicator_from_forest_to_forest = {
100
112
  "Multiple good indicators => run, 2 dict",
101
113
  ]
102
114
  )
103
- @patch(f"{class_path}.get_land_cover_terms", return_value=['cropland', 'seaOrOcean', 'forest'])
104
- def test_should_run(mock_download, resources, expected, num_inputs):
115
+ def test_should_run(resources: list, expected: bool, num_inputs: int):
105
116
  with open(f"{fixtures_folder}/multipleTransformations/impact-assessment.jsonld", encoding='utf-8') as f:
106
117
  impact = json.load(f)
107
118
 
@@ -146,7 +157,7 @@ def test_run_italy(*args):
146
157
  "region-europe not in the lookup file => default to region world"]
147
158
  )
148
159
  @patch(f"{class_path}._indicator", side_effect=fake_rounded_indicator)
149
- def test_run_with_country_fallback(mocked_indicator, added_data):
160
+ def test_run_with_country_fallback(mocked_indicator: Mock, added_data: dict):
150
161
  """
151
162
  When given valid sub-region or country not in the lookup file, default to country 'region-world' with value 574.56
152
163
  """
@@ -2,14 +2,14 @@ from unittest.mock import patch
2
2
  import json
3
3
  from tests.utils import fixtures_path, fake_new_indicator
4
4
 
5
- from hestia_earth.models.hyde32.landTransformationFromForest20YearAverageDuringCycle import TERM_ID, run
5
+ from hestia_earth.models.faostat2018 import MODEL
6
+ from hestia_earth.models.faostat2018.landTransformation100YearAverageDuringCycle import TERM_ID, run
6
7
 
7
- class_path = f"hestia_earth.models.hyde32.{TERM_ID}"
8
- fixtures_folder = f"{fixtures_path}/hyde32/{TERM_ID}"
8
+ class_path = f"hestia_earth.models.{MODEL}.{TERM_ID}"
9
+ fixtures_folder = f"{fixtures_path}/{MODEL}/{TERM_ID}"
9
10
 
10
11
 
11
- @patch('hestia_earth.models.hyde32.utils.get_land_cover_term_id', return_value='cropland')
12
- @patch('hestia_earth.models.hyde32.utils._new_indicator', side_effect=fake_new_indicator)
12
+ @patch(f"hestia_earth.models.{MODEL}.utils._new_indicator", side_effect=fake_new_indicator)
13
13
  def test_run(*args):
14
14
  with open(f"{fixtures_folder}/impact-assessment.jsonld", encoding='utf-8') as f:
15
15
  cycle = json.load(f)
@@ -2,14 +2,14 @@ from unittest.mock import patch
2
2
  import json
3
3
  from tests.utils import fixtures_path, fake_new_indicator
4
4
 
5
- from hestia_earth.models.hyde32.landTransformationFromForest100YearAverageDuringCycle import TERM_ID, run
5
+ from hestia_earth.models.faostat2018 import MODEL
6
+ from hestia_earth.models.faostat2018.landTransformation20YearAverageDuringCycle import TERM_ID, run
6
7
 
7
- class_path = f"hestia_earth.models.hyde32.{TERM_ID}"
8
- fixtures_folder = f"{fixtures_path}/hyde32/{TERM_ID}"
8
+ class_path = f"hestia_earth.models.{MODEL}.{TERM_ID}"
9
+ fixtures_folder = f"{fixtures_path}/{MODEL}/{TERM_ID}"
9
10
 
10
11
 
11
- @patch('hestia_earth.models.hyde32.utils.get_land_cover_term_id', return_value='cropland')
12
- @patch('hestia_earth.models.hyde32.utils._new_indicator', side_effect=fake_new_indicator)
12
+ @patch(f"hestia_earth.models.{MODEL}.utils._new_indicator", side_effect=fake_new_indicator)
13
13
  def test_run(*args):
14
14
  with open(f"{fixtures_folder}/impact-assessment.jsonld", encoding='utf-8') as f:
15
15
  cycle = json.load(f)
@@ -0,0 +1,28 @@
1
+ from hestia_earth.models.faostat2018.utils import should_run_landTransformationFromCropland
2
+
3
+
4
+ def test_should_run_landTransformationFromCropland():
5
+ impact = {}
6
+ term_id = 'term'
7
+
8
+ # no indicator => no run
9
+ should_run, *args = should_run_landTransformationFromCropland(term_id, impact)
10
+ assert not should_run
11
+
12
+ # with indicator value 0 => no run
13
+ impact['emissionsResourceUse'] = [{
14
+ 'term': {'@id': term_id},
15
+ 'previousLandCover': {'@id': 'cropland'},
16
+ 'value': 0
17
+ }]
18
+ should_run, *args = should_run_landTransformationFromCropland(term_id, impact)
19
+ assert not should_run
20
+
21
+ # with indicator => run
22
+ impact['emissionsResourceUse'] = [{
23
+ 'term': {'@id': term_id},
24
+ 'previousLandCover': {'@id': 'cropland'},
25
+ 'value': 10
26
+ }]
27
+ should_run, *args = should_run_landTransformationFromCropland(term_id, impact)
28
+ assert should_run is True
@@ -194,7 +194,8 @@ def test_get_sums_of_crop_expansion():
194
194
  "brazil_example",
195
195
  "zimbabwe_example",
196
196
  "brazil_empty_example",
197
- "gbr_example"
197
+ "gbr_example",
198
+ "malaysia"
198
199
  ]
199
200
  )
200
201
  @patch(f"{CLASS_PATH}._new_management", side_effect=fake_new_management)
@@ -0,0 +1,27 @@
1
+ import os
2
+ import json
3
+ import pytest
4
+ from unittest.mock import patch, Mock
5
+ from tests.utils import fixtures_path, fake_new_emission
6
+
7
+ from hestia_earth.models.hestia.seed_emissions import MODEL, MODEL_KEY, run
8
+
9
+ class_path = f"hestia_earth.models.{MODEL}.{MODEL_KEY}"
10
+ fixtures_folder = os.path.join(fixtures_path, MODEL, MODEL_KEY)
11
+
12
+ _folders = [d for d in os.listdir(fixtures_folder) if os.path.isdir(os.path.join(fixtures_folder, d))]
13
+
14
+
15
+ @pytest.mark.parametrize('folder', _folders)
16
+ @patch(f"{class_path}._new_emission", side_effect=fake_new_emission)
17
+ def test_run(mock_emission: Mock, folder: str):
18
+ fixture_path = os.path.join(fixtures_folder, folder)
19
+
20
+ with open(f"{fixture_path}/cycle.jsonld", encoding='utf-8') as f:
21
+ cycle = json.load(f)
22
+
23
+ with open(f"{fixture_path}/result.jsonld", encoding='utf-8') as f:
24
+ expected = json.load(f)
25
+
26
+ result = run(cycle)
27
+ assert result == expected, folder
@@ -12,12 +12,18 @@ from tests.utils import fake_new_measurement, fixtures_path
12
12
  class_path = f"hestia_earth.models.{MODEL}.{TERM_ID}"
13
13
  utils_path = f"hestia_earth.models.{MODEL}.biomass_utils"
14
14
  term_path = "hestia_earth.models.utils.term"
15
- property_path = "hestia_earth.models.utils.property"
16
-
17
15
  fixtures_folder = f"{fixtures_path}/{MODEL}/{TERM_ID}"
18
16
 
19
17
  _ITERATIONS = 1000
20
18
 
19
+ COVER_CROP_PROPERTY_TERM_IDS = [
20
+ "catchCrop",
21
+ "coverCrop",
22
+ "groundCover",
23
+ "longFallowCrop",
24
+ "shortFallowCrop"
25
+ ]
26
+
21
27
 
22
28
  def _load_fixture(path: str, default=None):
23
29
  if isfile(path):
@@ -40,6 +46,7 @@ PARAMS_SHOULD_RUN = [
40
46
  ("forest-to-cropland-lcc-q4", True),
41
47
  ("forest-to-gohac", False),
42
48
  ("forest-to-orchard", True),
49
+ ("forest-to-orchard-with-ground-cover", True), # Closes 989
43
50
  ("forest-to-orchard-with-backup-factors", True),
44
51
  ("forest-to-orchard-with-in-category-lcc", True),
45
52
  ("historical-land-cover-mix", True),
@@ -52,7 +59,14 @@ IDS_SHOULD_RUN = [p[0] for p in PARAMS_SHOULD_RUN]
52
59
 
53
60
 
54
61
  @mark.parametrize("subfolder, should_run", PARAMS_SHOULD_RUN, ids=IDS_SHOULD_RUN)
55
- def test_should_run(subfolder: str, should_run: bool):
62
+ @patch(f"{utils_path}.get_cover_crop_property_terms", return_value=COVER_CROP_PROPERTY_TERM_IDS)
63
+ @patch(f"{term_path}.search")
64
+ def test_should_run(
65
+ search_mock: MagicMock,
66
+ get_cover_crop_property_terms_mock: MagicMock,
67
+ subfolder: str,
68
+ should_run: bool
69
+ ):
56
70
  folder = f"{fixtures_folder}/{subfolder}"
57
71
 
58
72
  site = _load_fixture(f"{folder}/site.jsonld", {})
@@ -60,14 +74,22 @@ def test_should_run(subfolder: str, should_run: bool):
60
74
  result, *_ = _should_run(site)
61
75
  assert result == should_run
62
76
 
77
+ assert get_cover_crop_property_terms_mock.call_count <= 1 # assert the API call is only requested once
78
+ search_mock.assert_not_called() # assert the API call is properly mocked
63
79
 
64
- def test_should_run_no_data():
80
+
81
+ @patch(f"{utils_path}.get_cover_crop_property_terms", return_value=COVER_CROP_PROPERTY_TERM_IDS)
82
+ @patch(f"{term_path}.search")
83
+ def test_should_run_no_data(search_mock: MagicMock, get_cover_crop_property_terms_mock: MagicMock,):
65
84
  SITE = {}
66
85
  EXPECTED = False
67
86
 
68
87
  result, *_ = _should_run(SITE)
69
88
  assert result == EXPECTED
70
89
 
90
+ assert get_cover_crop_property_terms_mock.call_count <= 1 # assert the API call is only performed once
91
+ search_mock.assert_not_called() # assert the API call is properly mocked
92
+
71
93
 
72
94
  PARAMS_RUN = [subfolder for subfolder, should_run in PARAMS_SHOULD_RUN if should_run]
73
95
 
@@ -76,7 +98,11 @@ PARAMS_RUN = [subfolder for subfolder, should_run in PARAMS_SHOULD_RUN if should
76
98
  @patch(f"{class_path}.calc_descriptive_stats", side_effect=_fake_calc_descriptive_stats)
77
99
  @patch(f"{class_path}._new_measurement", side_effect=fake_new_measurement)
78
100
  @patch(f"{utils_path}._get_sample_func", return_value=sample_constant)
101
+ @patch(f"{utils_path}.get_cover_crop_property_terms", return_value=COVER_CROP_PROPERTY_TERM_IDS)
102
+ @patch(f"{term_path}.search")
79
103
  def test_run(
104
+ search_mock: MagicMock,
105
+ get_cover_crop_property_terms_mock: MagicMock,
80
106
  _get_sample_func_mock: MagicMock,
81
107
  _new_measurement_mock: MagicMock,
82
108
  _calc_descriptive_stats_mock: MagicMock,
@@ -92,6 +118,9 @@ def test_run(
92
118
 
93
119
  assert result == expected
94
120
 
121
+ assert get_cover_crop_property_terms_mock.call_count <= 1 # assert the API call is only performed once
122
+ search_mock.assert_not_called() # assert the API call is properly mocked
123
+
95
124
 
96
125
  # subfolder
97
126
  PARAMS_RUN_WITH_STATS = [
@@ -103,7 +132,11 @@ PARAMS_RUN_WITH_STATS = [
103
132
 
104
133
  @mark.parametrize("subfolder", PARAMS_RUN_WITH_STATS)
105
134
  @patch(f"{class_path}._new_measurement", side_effect=fake_new_measurement)
135
+ @patch(f"{utils_path}.get_cover_crop_property_terms", return_value=COVER_CROP_PROPERTY_TERM_IDS)
136
+ @patch(f"{term_path}.search")
106
137
  def test_run_with_stats(
138
+ search_mock: MagicMock,
139
+ get_cover_crop_property_terms_mock: MagicMock,
107
140
  _new_measurement_mock: MagicMock,
108
141
  subfolder: str
109
142
  ):
@@ -117,6 +150,9 @@ def test_run_with_stats(
117
150
 
118
151
  assert result == expected
119
152
 
153
+ assert get_cover_crop_property_terms_mock.call_count <= 1 # assert the API call is only performed once
154
+ search_mock.assert_not_called() # assert the API call is properly mocked
155
+
120
156
 
121
157
  # input, expected
122
158
  PARAMS_BUILD_COLUMN_NAME = [
@@ -12,12 +12,18 @@ from tests.utils import fake_new_measurement, fixtures_path
12
12
  class_path = f"hestia_earth.models.{MODEL}.{TERM_ID}"
13
13
  utils_path = f"hestia_earth.models.{MODEL}.biomass_utils"
14
14
  term_path = "hestia_earth.models.utils.term"
15
- property_path = "hestia_earth.models.utils.property"
16
-
17
15
  fixtures_folder = f"{fixtures_path}/{MODEL}/{TERM_ID}"
18
16
 
19
17
  _ITERATIONS = 1000
20
18
 
19
+ COVER_CROP_PROPERTY_TERM_IDS = [
20
+ "catchCrop",
21
+ "coverCrop",
22
+ "groundCover",
23
+ "longFallowCrop",
24
+ "shortFallowCrop"
25
+ ]
26
+
21
27
 
22
28
  def _load_fixture(path: str, default=None):
23
29
  if isfile(path):
@@ -41,6 +47,7 @@ PARAMS_SHOULD_RUN = [
41
47
  ("forest-to-cropland-lcc-q4", True),
42
48
  ("forest-to-gohac", False),
43
49
  ("forest-to-orchard", True),
50
+ ("forest-to-orchard-with-ground-cover", True), # Closes 989
44
51
  ("forest-to-orchard-with-in-category-lcc", True),
45
52
  ("historical-land-cover-mix", True),
46
53
  ("historical-argentina-pasture", True),
@@ -52,7 +59,14 @@ IDS_SHOULD_RUN = [p[0] for p in PARAMS_SHOULD_RUN]
52
59
 
53
60
 
54
61
  @mark.parametrize("subfolder, should_run", PARAMS_SHOULD_RUN, ids=IDS_SHOULD_RUN)
55
- def test_should_run(subfolder: str, should_run: bool):
62
+ @patch(f"{utils_path}.get_cover_crop_property_terms", return_value=COVER_CROP_PROPERTY_TERM_IDS)
63
+ @patch(f"{term_path}.search")
64
+ def test_should_run(
65
+ search_mock: MagicMock,
66
+ get_cover_crop_property_terms_mock: MagicMock,
67
+ subfolder: str,
68
+ should_run: bool
69
+ ):
56
70
  folder = f"{fixtures_folder}/{subfolder}"
57
71
 
58
72
  site = _load_fixture(f"{folder}/site.jsonld", {})
@@ -60,14 +74,22 @@ def test_should_run(subfolder: str, should_run: bool):
60
74
  result, *_ = _should_run(site)
61
75
  assert result == should_run
62
76
 
77
+ assert get_cover_crop_property_terms_mock.call_count <= 1 # assert the API call is only requested once
78
+ search_mock.assert_not_called() # assert the API call is properly mocked
63
79
 
64
- def test_should_run_no_data():
80
+
81
+ @patch(f"{utils_path}.get_cover_crop_property_terms", return_value=COVER_CROP_PROPERTY_TERM_IDS)
82
+ @patch(f"{term_path}.search")
83
+ def test_should_run_no_data(search_mock: MagicMock, get_cover_crop_property_terms_mock: MagicMock):
65
84
  SITE = {}
66
85
  EXPECTED = False
67
86
 
68
87
  result, *_ = _should_run(SITE)
69
88
  assert result == EXPECTED
70
89
 
90
+ assert get_cover_crop_property_terms_mock.call_count <= 1 # assert the API call is only requested once
91
+ search_mock.assert_not_called() # assert the API call is properly mocked
92
+
71
93
 
72
94
  PARAMS_RUN = [subfolder for subfolder, should_run in PARAMS_SHOULD_RUN if should_run]
73
95
 
@@ -76,7 +98,11 @@ PARAMS_RUN = [subfolder for subfolder, should_run in PARAMS_SHOULD_RUN if should
76
98
  @patch(f"{class_path}.calc_descriptive_stats", side_effect=_fake_calc_descriptive_stats)
77
99
  @patch(f"{class_path}._new_measurement", side_effect=fake_new_measurement)
78
100
  @patch(f"{utils_path}._get_sample_func", return_value=sample_constant)
101
+ @patch(f"{utils_path}.get_cover_crop_property_terms", return_value=COVER_CROP_PROPERTY_TERM_IDS)
102
+ @patch(f"{term_path}.search")
79
103
  def test_run(
104
+ search_mock: MagicMock,
105
+ get_cover_crop_property_terms_mock: MagicMock,
80
106
  _get_sample_func_mock: MagicMock,
81
107
  _new_measurement_mock: MagicMock,
82
108
  _calc_descriptive_stats_mock: MagicMock,
@@ -92,6 +118,9 @@ def test_run(
92
118
 
93
119
  assert result == expected
94
120
 
121
+ assert get_cover_crop_property_terms_mock.call_count <= 1 # assert the API call is only requested once
122
+ search_mock.assert_not_called() # assert the API call is properly mocked
123
+
95
124
 
96
125
  # subfolder
97
126
  PARAMS_RUN_WITH_STATS = [
@@ -103,7 +132,11 @@ PARAMS_RUN_WITH_STATS = [
103
132
 
104
133
  @mark.parametrize("subfolder", PARAMS_RUN_WITH_STATS)
105
134
  @patch(f"{class_path}._new_measurement", side_effect=fake_new_measurement)
135
+ @patch(f"{utils_path}.get_cover_crop_property_terms", return_value=COVER_CROP_PROPERTY_TERM_IDS)
136
+ @patch(f"{term_path}.search")
106
137
  def test_run_with_stats(
138
+ search_mock: MagicMock,
139
+ get_cover_crop_property_terms_mock: MagicMock,
107
140
  _new_measurement_mock: MagicMock,
108
141
  subfolder: str
109
142
  ):
@@ -117,6 +150,9 @@ def test_run_with_stats(
117
150
 
118
151
  assert result == expected
119
152
 
153
+ assert get_cover_crop_property_terms_mock.call_count <= 1 # assert the API call is only requested once
154
+ search_mock.assert_not_called() # assert the API call is properly mocked
155
+
120
156
 
121
157
  # input, expected
122
158
  PARAMS_BUILD_COLUMN_NAME = [
@@ -2,7 +2,7 @@ from functools import reduce
2
2
  import json
3
3
  from os.path import isfile
4
4
  from pytest import mark
5
- from unittest.mock import patch
5
+ from unittest.mock import MagicMock, patch
6
6
 
7
7
  from hestia_earth.models.ipcc2019.co2ToAirAboveGroundBiomassStockChange import MODEL, run
8
8
 
@@ -10,8 +10,26 @@ from tests.utils import fake_new_emission, fixtures_path, order_list
10
10
 
11
11
  class_path = f"hestia_earth.models.{MODEL}.co2ToAirAboveGroundBiomassStockChange"
12
12
  utils_path = f"hestia_earth.models.{MODEL}.co2ToAirCarbonStockChange_utils"
13
+ biomass_utils_path = f"hestia_earth.models.{MODEL}.biomass_utils"
14
+ term_path = "hestia_earth.models.utils.term"
13
15
  fixtures_folder = f"{fixtures_path}/{MODEL}/co2ToAirAboveGroundBiomassStockChange"
14
16
 
17
+ COVER_CROP_PROPERTY_TERM_IDS = [
18
+ "catchCrop",
19
+ "coverCrop",
20
+ "groundCover",
21
+ "longFallowCrop",
22
+ "shortFallowCrop"
23
+ ]
24
+
25
+
26
+ def _load_fixture(path: str, default=None):
27
+ if isfile(path):
28
+ with open(path, encoding="utf-8") as f:
29
+ return json.load(f)
30
+ return default
31
+
32
+
15
33
  RUN_SCENARIOS = [
16
34
  ("no-overlapping-cycles", 3),
17
35
  ("overlapping-cycles", 4),
@@ -22,18 +40,12 @@ RUN_SCENARIOS = [
22
40
  ("multiple-method-classifications", 5), # Closes issue #764
23
41
  ("non-soil-based-gohac-system", 3), # Closes issue #848
24
42
  ("soil-based-gohac-system", 3), # Closes issue #848
25
- ("with-gapfilled-start-date-end-date", 1) # Closes issue #972
43
+ ("with-gapfilled-start-date-end-date", 1), # Closes issue #972
44
+ ("forest-to-orchard-with-ground-cover", 3) # Closes issue #989
26
45
  ]
27
46
  """List of (subfolder: str, num_cycles: int)."""
28
47
 
29
48
 
30
- def _load_fixture(path: str, default=None):
31
- if isfile(path):
32
- with open(path, encoding="utf-8") as f:
33
- return json.load(f)
34
- return default
35
-
36
-
37
49
  RUN_PARAMS = reduce(
38
50
  lambda params, scenario: params + [(scenario[0], scenario[1], i) for i in range(scenario[1])],
39
51
  RUN_SCENARIOS,
@@ -47,8 +59,19 @@ RUN_IDS = [f"{param[0]}, cycle{param[2]}" for param in RUN_PARAMS]
47
59
  @mark.parametrize("subfolder, num_cycles, cycle_index", RUN_PARAMS, ids=RUN_IDS)
48
60
  @patch(f"{class_path}._new_emission", side_effect=fake_new_emission)
49
61
  @patch(f"{utils_path}.related_cycles")
50
- @patch(f"{utils_path}._get_site") # TODO: rationalise order of patches
51
- def test_run(_get_site_mock, related_cycles_mock, _new_emission_mock, subfolder, num_cycles, cycle_index):
62
+ @patch(f"{utils_path}._get_site")
63
+ @patch(f"{biomass_utils_path}.get_cover_crop_property_terms", return_value=COVER_CROP_PROPERTY_TERM_IDS)
64
+ @patch(f"{term_path}.search")
65
+ def test_run(
66
+ search_mock: MagicMock,
67
+ get_cover_crop_property_terms_mock: MagicMock,
68
+ get_site_mock: MagicMock,
69
+ related_cycles_mock: MagicMock,
70
+ _new_emission_mock: MagicMock,
71
+ subfolder: str,
72
+ num_cycles: int,
73
+ cycle_index: int
74
+ ):
52
75
  """
53
76
  Test `run` function for each cycle in each scenario.
54
77
  """
@@ -60,25 +83,39 @@ def test_run(_get_site_mock, related_cycles_mock, _new_emission_mock, subfolder,
60
83
  _load_fixture(f"{fixtures_folder}/{subfolder}/cycle{i}.jsonld") for i in range(num_cycles)
61
84
  ]
62
85
 
63
- _get_site_mock.return_value = site
86
+ get_site_mock.return_value = site
64
87
  related_cycles_mock.return_value = cycles
65
88
 
66
89
  result = run(cycle)
67
90
  assert order_list(result) == order_list(expected)
68
91
 
92
+ assert get_cover_crop_property_terms_mock.call_count <= 1 # assert the API call is only requested once
93
+ search_mock.assert_not_called() # assert the API call is properly mocked
94
+
69
95
 
70
96
  @patch(f"{class_path}._new_emission", side_effect=fake_new_emission)
71
97
  @patch(f"{utils_path}.related_cycles")
72
- @patch(f"{utils_path}._get_site") # TODO: rationalise order of patches
73
- def test_run_empty(_get_site_mock, related_cycles_mock, _new_emission_mock):
98
+ @patch(f"{utils_path}._get_site")
99
+ @patch(f"{biomass_utils_path}.get_cover_crop_property_terms", return_value=COVER_CROP_PROPERTY_TERM_IDS)
100
+ @patch(f"{term_path}.search")
101
+ def test_run_empty(
102
+ search_mock: MagicMock,
103
+ get_cover_crop_property_terms_mock: MagicMock,
104
+ get_site_mock: MagicMock,
105
+ related_cycles_mock: MagicMock,
106
+ _new_emission_mock: MagicMock
107
+ ):
74
108
  """
75
109
  Test `run` function for each cycle in each scenario.
76
110
  """
77
111
  CYCLE = {}
78
112
  EXPECTED = []
79
113
 
80
- _get_site_mock.return_value = {}
114
+ get_site_mock.return_value = {}
81
115
  related_cycles_mock.return_value = [CYCLE]
82
116
 
83
117
  result = run(CYCLE)
84
118
  assert result == EXPECTED
119
+
120
+ assert get_cover_crop_property_terms_mock.call_count <= 1 # assert the API call is only requested once
121
+ search_mock.assert_not_called() # assert the API call is properly mocked