hestia-earth-models 0.59.5__py3-none-any.whl → 0.59.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.

Potentially problematic release.


This version of hestia-earth-models might be problematic. Click here for more details.

Files changed (46) hide show
  1. hestia_earth/models/cache_sites.py +6 -8
  2. hestia_earth/models/cycle/animal/milkYield.py +86 -0
  3. hestia_earth/models/cycle/endDate.py +50 -0
  4. hestia_earth/models/cycle/inorganicFertiliser.py +3 -2
  5. hestia_earth/models/cycle/milkYield.py +8 -3
  6. hestia_earth/models/cycle/pre_checks/__init__.py +1 -2
  7. hestia_earth/models/cycle/siteDuration.py +1 -1
  8. hestia_earth/models/cycle/startDate.py +42 -0
  9. hestia_earth/models/emepEea2019/nh3ToAirInorganicFertiliser.py +9 -6
  10. hestia_earth/models/faostat2018/liveweightPerHead.py +77 -41
  11. hestia_earth/models/faostat2018/product/price.py +43 -57
  12. hestia_earth/models/faostat2018/utils.py +10 -2
  13. hestia_earth/models/geospatialDatabase/utils.py +0 -1
  14. hestia_earth/models/haversineFormula/transport/distance.py +6 -3
  15. hestia_earth/models/ipcc2006/n2OToAirInorganicFertiliserIndirect.py +1 -1
  16. hestia_earth/models/ipcc2019/co2ToAirLimeHydrolysis.py +1 -1
  17. hestia_earth/models/ipcc2019/co2ToAirUreaHydrolysis.py +2 -7
  18. hestia_earth/models/ipcc2019/organicCarbonPerHa.py +26 -6
  19. hestia_earth/models/ipcc2019/pastureGrass.py +2 -1
  20. hestia_earth/models/linkedImpactAssessment/__init__.py +3 -3
  21. hestia_earth/models/mocking/search-results.json +244 -232
  22. hestia_earth/models/schmidt2007/h2SToAirWasteTreatment.py +58 -0
  23. hestia_earth/models/site/management.py +3 -1
  24. hestia_earth/models/site/pre_checks/cache_geospatialDatabase.py +2 -2
  25. hestia_earth/models/utils/__init__.py +4 -1
  26. hestia_earth/models/utils/animalProduct.py +6 -4
  27. hestia_earth/models/utils/blank_node.py +3 -2
  28. hestia_earth/models/utils/product.py +9 -1
  29. hestia_earth/models/utils/property.py +2 -1
  30. hestia_earth/models/utils/site.py +7 -4
  31. hestia_earth/models/version.py +1 -1
  32. {hestia_earth_models-0.59.5.dist-info → hestia_earth_models-0.59.7.dist-info}/METADATA +1 -1
  33. {hestia_earth_models-0.59.5.dist-info → hestia_earth_models-0.59.7.dist-info}/RECORD +44 -38
  34. tests/models/cycle/animal/test_milkYield.py +43 -0
  35. tests/models/cycle/test_endDate.py +24 -0
  36. tests/models/cycle/test_startDate.py +22 -0
  37. tests/models/faostat2018/product/test_price.py +40 -48
  38. tests/models/faostat2018/test_liveweightPerHead.py +106 -42
  39. tests/models/ipcc2019/test_organicCarbonPerHa.py +102 -39
  40. tests/models/schmidt2007/test_h2SToAirWasteTreatment.py +45 -0
  41. tests/models/utils/test_blank_node.py +71 -3
  42. hestia_earth/models/cycle/pre_checks/startDate.py +0 -52
  43. tests/models/cycle/pre_checks/test_startDate.py +0 -44
  44. {hestia_earth_models-0.59.5.dist-info → hestia_earth_models-0.59.7.dist-info}/LICENSE +0 -0
  45. {hestia_earth_models-0.59.5.dist-info → hestia_earth_models-0.59.7.dist-info}/WHEEL +0 -0
  46. {hestia_earth_models-0.59.5.dist-info → hestia_earth_models-0.59.7.dist-info}/top_level.txt +0 -0
@@ -2,22 +2,12 @@ import json
2
2
  from unittest.mock import patch
3
3
  from tests.utils import fixtures_path
4
4
 
5
- from hestia_earth.models.faostat2018.product.price import MODEL, MODEL_KEY, run, _should_run, _lookup_data
5
+ from hestia_earth.models.faostat2018.product.price import MODEL, MODEL_KEY, run, _lookup_data
6
6
 
7
7
  class_path = f"hestia_earth.models.{MODEL}.product.{MODEL_KEY}"
8
8
  fixtures_folder = f"{fixtures_path}/{MODEL}/product/{MODEL_KEY}"
9
9
 
10
10
 
11
- def test_should_run():
12
- cycle = {'endDate': '2020-01'}
13
- should_run, *_ = _should_run(cycle)
14
- assert not should_run
15
-
16
- cycle['site'] = {'country': {'@id': 'GADM-GBR'}}
17
- should_run, *_ = _should_run(cycle)
18
- assert should_run is True
19
-
20
-
21
11
  def test_lookup_data():
22
12
  assert _lookup_data('cocoaSeedDehulled', 'Cocoa beans', 'GADM-GHA', 2000, term_type='crop') == 412.9
23
13
  # average price per tonne as year value is missing
@@ -37,11 +27,23 @@ def test_run_crop(*args):
37
27
 
38
28
 
39
29
  @patch(f"{class_path}.download_hestia", return_value={})
40
- def test_run_animalProduct(*args):
41
- with open(f"{fixtures_folder}/animalProduct/cycle.jsonld", encoding='utf-8') as f:
30
+ def test_run_animalProduct_kg(*args):
31
+ with open(f"{fixtures_folder}/animalProduct/kg/cycle.jsonld", encoding='utf-8') as f:
32
+ cycle = json.load(f)
33
+
34
+ with open(f"{fixtures_folder}/animalProduct/kg/result.jsonld", encoding='utf-8') as f:
35
+ expected = json.load(f)
36
+
37
+ value = run(cycle)
38
+ assert value == expected
39
+
40
+
41
+ @patch(f"{class_path}.download_hestia", return_value={})
42
+ def test_run_animalProduct_number(*args):
43
+ with open(f"{fixtures_folder}/animalProduct/number/cycle.jsonld", encoding='utf-8') as f:
42
44
  cycle = json.load(f)
43
45
 
44
- with open(f"{fixtures_folder}/animalProduct/result.jsonld", encoding='utf-8') as f:
46
+ with open(f"{fixtures_folder}/animalProduct/number/result.jsonld", encoding='utf-8') as f:
45
47
  expected = json.load(f)
46
48
 
47
49
  value = run(cycle)
@@ -57,24 +59,10 @@ def test_run_liveAnimal_chicken(mock_download_hestia):
57
59
  expected = json.load(f)
58
60
 
59
61
  mock_download_hestia.return_value = {
60
- "@id": "meatChickenReadyToCookWeight",
62
+ "@id": "meatChickenLiveweight",
61
63
  "@type": "Term",
62
- "units": "kg ready-to-cook weight",
63
- "termType": "animalProduct",
64
- "defaultProperties": [
65
- {
66
- "term": {
67
- "@type": "Term",
68
- "name": "Processing conversion, cold carcass weight to ready-to-cook weight",
69
- "termType": "property",
70
- "@id": "processingConversionColdCarcassWeightToReadyToCookWeight",
71
- "units": "%"
72
- },
73
- "value": 72.45065789473684,
74
- "sd": 3.605314180391945,
75
- "@type": "Property"
76
- }
77
- ]
64
+ "units": "kg liveweight",
65
+ "termType": "animalProduct"
78
66
  }
79
67
  value = run(cycle)
80
68
  assert value == expected
@@ -89,24 +77,28 @@ def test_run_liveAnimal_pig(mock_download_hestia):
89
77
  expected = json.load(f)
90
78
 
91
79
  mock_download_hestia.return_value = {
92
- "@id": "meatPigColdDressedCarcassWeight",
80
+ "@id": "meatPigLiveweight",
81
+ "@type": "Term",
82
+ "units": "kg liveweight",
83
+ "termType": "animalProduct"
84
+ }
85
+ value = run(cycle)
86
+ assert value == expected
87
+
88
+
89
+ @patch(f"{class_path}.download_hestia")
90
+ def test_run_liveAnimal_sheepLamb(mock_download_hestia):
91
+ with open(f"{fixtures_folder}/liveAnimal/sheepLamb/cycle.jsonld", encoding='utf-8') as f:
92
+ cycle = json.load(f)
93
+
94
+ with open(f"{fixtures_folder}/liveAnimal/sheepLamb/result.jsonld", encoding='utf-8') as f:
95
+ expected = json.load(f)
96
+
97
+ mock_download_hestia.return_value = {
98
+ "@id": "meatSheepLiveweight",
93
99
  "@type": "Term",
94
- "units": "kg cold dressed carcass weight",
95
- "termType": "animalProduct",
96
- "defaultProperties": [
97
- {
98
- "term": {
99
- "@type": "Term",
100
- "name": "Processing conversion, liveweight to cold carcass weight",
101
- "termType": "property",
102
- "@id": "processingConversionLiveweightToColdCarcassWeight",
103
- "units": "%"
104
- },
105
- "value": 75.22666597366735,
106
- "sd": 2.9377280394802323,
107
- "@type": "Property"
108
- }
109
- ]
100
+ "units": "kg liveweight",
101
+ "termType": "animalProduct"
110
102
  }
111
103
  value = run(cycle)
112
104
  assert value == expected
@@ -1,61 +1,125 @@
1
1
  from unittest.mock import patch
2
2
  import json
3
3
 
4
- from hestia_earth.schema import TermTermType
5
4
  from tests.utils import fixtures_path, fake_new_property
6
5
 
7
- from hestia_earth.models.faostat2018.liveweightPerHead import MODEL, TERM_ID, _should_run, run
6
+ from hestia_earth.models.faostat2018.liveweightPerHead import MODEL, TERM_ID, run
8
7
 
9
8
  class_path = f"hestia_earth.models.{MODEL}.{TERM_ID}"
10
9
  fixtures_folder = f"{fixtures_path}/{MODEL}/{TERM_ID}"
11
- FAO_TERM = {
12
- '@id': 'meatChickenReadyToCookWeight',
13
- '@type': 'Term',
14
- 'units': 'kg ready-to-cook weight',
15
- 'defaultProperties': [{
16
- 'term': {'@id': 'processingConversionLiveweightToColdCarcassWeight', 'units': '%'},
17
- 'value': 72.30401869158878
18
- }, {
19
- 'term': {'@id': 'processingConversionColdCarcassWeightToReadyToCookWeight', 'units': '%'},
20
- 'value': 72.45065789473684
21
- }]
22
- }
23
-
24
-
25
- def test_should_run():
26
- cycle = {}
27
-
28
- # no animal products => no run
29
- should_run, *args = _should_run(cycle)
30
- assert not should_run
31
-
32
- # with animal products => no run
33
- cycle['products'] = [
34
- {
35
- 'term': {
36
- 'termType': TermTermType.ANIMALPRODUCT.value,
37
- 'units': 'kg liveweight'
10
+
11
+
12
+ @patch(f"{class_path}._new_property", side_effect=fake_new_property)
13
+ def test_run_animalProduct(*args):
14
+ with open(f"{fixtures_folder}/animalProduct/cycle.jsonld", encoding='utf-8') as f:
15
+ cycle = json.load(f)
16
+
17
+ with open(f"{fixtures_folder}/animalProduct/result.jsonld", encoding='utf-8') as f:
18
+ expected = json.load(f)
19
+
20
+ value = run(cycle)
21
+ assert value == expected
22
+
23
+
24
+ @patch(f"{class_path}._new_property", side_effect=fake_new_property)
25
+ @patch(f"{class_path}.download_hestia")
26
+ def test_run_liveAnimal_chicken(mock_download_hestia, *args):
27
+ with open(f"{fixtures_folder}/liveAnimal/chicken/cycle.jsonld", encoding='utf-8') as f:
28
+ cycle = json.load(f)
29
+
30
+ with open(f"{fixtures_folder}/liveAnimal/chicken/result.jsonld", encoding='utf-8') as f:
31
+ expected = json.load(f)
32
+
33
+ mock_download_hestia.return_value = {
34
+ "@id": "meatChickenReadyToCookWeight",
35
+ "@type": "Term",
36
+ "units": "kg ready-to-cook weight",
37
+ "defaultProperties": [
38
+ {
39
+ "term": {
40
+ "@type": "Term",
41
+ "name": "Processing conversion, liveweight to cold carcass weight",
42
+ "termType": "property",
43
+ "@id": "processingConversionLiveweightToColdCarcassWeight",
44
+ "units": "%"
45
+ },
46
+ "value": 72.30401869158878,
47
+ "@type": "Property"
48
+ },
49
+ {
50
+ "term": {
51
+ "@type": "Term",
52
+ "name": "Processing conversion, cold carcass weight to ready-to-cook weight",
53
+ "termType": "property",
54
+ "@id": "processingConversionColdCarcassWeightToReadyToCookWeight",
55
+ "units": "%"
56
+ },
57
+ "value": 72.45065789473684,
58
+ "@type": "Property"
38
59
  }
39
- }
40
- ]
41
- should_run, *args = _should_run(cycle)
42
- assert not should_run
60
+ ]
61
+ }
62
+ value = run(cycle)
63
+ assert value == expected
64
+
65
+
66
+ @patch(f"{class_path}._new_property", side_effect=fake_new_property)
67
+ @patch(f"{class_path}.download_hestia")
68
+ def test_run_liveAnimal_pig(mock_download_hestia, *args):
69
+ with open(f"{fixtures_folder}/liveAnimal/pig/cycle.jsonld", encoding='utf-8') as f:
70
+ cycle = json.load(f)
43
71
 
44
- # with an endDate and country => run
45
- cycle['endDate'] = '2020'
46
- cycle['site'] = {'country': {'@id': 'GADM-GBR'}}
47
- should_run, *args = _should_run(cycle)
48
- assert should_run is True
72
+ with open(f"{fixtures_folder}/liveAnimal/pig/result.jsonld", encoding='utf-8') as f:
73
+ expected = json.load(f)
74
+
75
+ mock_download_hestia.return_value = {
76
+ "@id": "meatPigColdDressedCarcassWeight",
77
+ "@type": "Term",
78
+ "units": "kg cold dressed carcass weight",
79
+ "defaultProperties": [
80
+ {
81
+ "term": {
82
+ "@type": "Term",
83
+ "name": "Processing conversion, liveweight to cold carcass weight",
84
+ "termType": "property",
85
+ "@id": "processingConversionLiveweightToColdCarcassWeight",
86
+ "units": "%"
87
+ },
88
+ "value": 75.22666597366735,
89
+ "@type": "Property"
90
+ }
91
+ ]
92
+ }
93
+ value = run(cycle)
94
+ assert value == expected
49
95
 
50
96
 
51
- @patch(f"hestia_earth.models.{MODEL}.utils.download_hestia", return_value=FAO_TERM)
52
97
  @patch(f"{class_path}._new_property", side_effect=fake_new_property)
53
- def test_run(*args):
54
- with open(f"{fixtures_folder}/cycle.jsonld", encoding='utf-8') as f:
98
+ @patch(f"{class_path}.download_hestia")
99
+ def test_run_liveAnimal_sheepLamb(mock_download_hestia, *args):
100
+ with open(f"{fixtures_folder}/liveAnimal/sheepLamb/cycle.jsonld", encoding='utf-8') as f:
55
101
  cycle = json.load(f)
56
102
 
57
- with open(f"{fixtures_folder}/result.jsonld", encoding='utf-8') as f:
103
+ with open(f"{fixtures_folder}/liveAnimal/sheepLamb/result.jsonld", encoding='utf-8') as f:
58
104
  expected = json.load(f)
59
105
 
106
+ mock_download_hestia.return_value = {
107
+ "@id": "meatSheepColdDressedCarcassWeight",
108
+ "@type": "Term",
109
+ "units": "kg cold dressed carcass weight",
110
+ "defaultProperties": [
111
+ {
112
+ "term": {
113
+ "@type": "Term",
114
+ "name": "Processing conversion, liveweight to cold carcass weight",
115
+ "termType": "property",
116
+ "@id": "processingConversionLiveweightToColdCarcassWeight",
117
+ "units": "%"
118
+ },
119
+ "value": 47.301833667687326,
120
+ "@type": "Property"
121
+ }
122
+ ]
123
+ }
60
124
  value = run(cycle)
61
125
  assert value == expected
@@ -20,6 +20,7 @@ from hestia_earth.models.ipcc2019.organicCarbonPerHa import (
20
20
  _check_cropland_medium_category,
21
21
  _get_carbon_input_kwargs,
22
22
  _iterate_soc_equilibriums,
23
+ _should_run,
23
24
  IpccCarbonInputCategory,
24
25
  IpccLandUseCategory,
25
26
  IpccManagementCategory,
@@ -103,36 +104,38 @@ def find_term_property_side_effect(term: dict, property: str, *_):
103
104
  # --- TIER 1 & TIER 2 TESTS ---
104
105
 
105
106
 
106
- # subfolder, load_cycles
107
- RUN_SUBFOLDERS = [
108
- ("tier-1-and-2/cropland", True),
109
- ("tier-2/with-generalised-monthly-measurements", True), # Closes issue 600
110
- ("tier-2/with-incomplete-climate-data", True), # Closes issue 599
111
- ("tier-2/with-initial-soc", True),
112
- ("tier-2/with-multi-year-cycles", True),
113
- ("tier-2/with-multi-year-cycles-and-missing-properties", True), # Closes issue 734
114
- ("tier-2/without-any-measurements", True), # Closes issue 594
115
- ("tier-2/without-initial-soc", True),
116
- ("tier-2/with-irrigation", True),
117
- ("tier-2/with-irrigation-dates", True),
118
- ("tier-2/with-paddy-rice", True),
119
- ("tier-2/with-sand-without-date", True), # Closes issue 739
120
- ("tier-2/with-irrigated-upland-rice", True),
121
- ("tier-1/cropland-depth-as-float", False),
122
- ("tier-1/cropland-with-measured-soc", False),
123
- ("tier-1/cropland-without-measured-soc", False),
124
- ("tier-1/permanent-pasture", False),
125
- ("tier-1/should-not-run", False),
126
- ("tier-1/without-management-with-measured-soc", False),
127
- ("tier-1/land-use-change", False), # Closes issue 755
128
- ("tier-1/run-with-site-type", False) # Closes issue 755
107
+ # subfolder, load_cycles, should_run
108
+ SHOULD_RUN_SUBFOLDERS = [
109
+ ("tier-1-and-2/cropland", True, True),
110
+ ("tier-1-and-2/with-zero-carbon-input", True, True), # Closes issue 777
111
+ ("tier-2/with-generalised-monthly-measurements", True, False), # Closes issue 600
112
+ ("tier-2/with-incomplete-climate-data", True, False), # Closes issue 599
113
+ ("tier-2/with-initial-soc", True, True),
114
+ ("tier-2/with-multi-year-cycles", True, True),
115
+ ("tier-2/with-multi-year-cycles-and-missing-properties", True, True), # Closes issue 734
116
+ ("tier-2/without-any-measurements", True, False), # Closes issue 594
117
+ ("tier-2/without-initial-soc", True, True),
118
+ ("tier-2/with-irrigation", True, True),
119
+ ("tier-2/with-irrigation-dates", True, True),
120
+ ("tier-2/with-paddy-rice", True, False),
121
+ ("tier-2/with-sand-without-date", True, True), # Closes issue 739
122
+ ("tier-2/with-irrigated-upland-rice", True, False),
123
+ ("tier-1/cropland-depth-as-float", False, True),
124
+ ("tier-1/cropland-with-measured-soc", False, True),
125
+ ("tier-1/cropland-without-measured-soc", False, True),
126
+ ("tier-1/permanent-pasture", False, True),
127
+ ("tier-1/should-not-run", False, False),
128
+ ("tier-1/without-management-with-measured-soc", False, False),
129
+ ("tier-1/land-use-change", False, True), # Closes issue 755
130
+ ("tier-1/run-with-site-type", False, True), # Closes issue 755
131
+ ("tier-1/cropland-polar", False, False) # Closes issue 794
129
132
  ]
130
133
 
131
134
 
132
135
  @mark.parametrize(
133
- "subfolder, load_cycles",
134
- RUN_SUBFOLDERS,
135
- ids=[params[0] for params in RUN_SUBFOLDERS]
136
+ "subfolder, load_cycles, should_run",
137
+ SHOULD_RUN_SUBFOLDERS,
138
+ ids=[params[0] for params in SHOULD_RUN_SUBFOLDERS]
136
139
  )
137
140
  @patch(f"{class_path}._new_measurement", side_effect=fake_new_measurement)
138
141
  @patch(f"{class_path}.get_cover_crop_property_terms", return_value=COVER_CROP_PROPERTY_TERM_IDS)
@@ -142,8 +145,8 @@ RUN_SUBFOLDERS = [
142
145
  @patch(f"{class_path}.get_upland_rice_land_cover_terms", return_value=UPLAND_RICE_LAND_COVER_TERM_IDS)
143
146
  @patch(f"{class_path}.get_upland_rice_crop_terms", return_value=UPLAND_RICE_CROP_TERM_IDS)
144
147
  @patch(f"{class_path}.related_cycles")
145
- @patch("hestia_earth.models.utils.property.find_term_property")
146
- def test_run(
148
+ @patch("hestia_earth.models.utils.property.find_term_property", side_effect=find_term_property_side_effect)
149
+ def test_should_run(
147
150
  mock_find_term_property,
148
151
  mock_related_cycles,
149
152
  _mock_get_upland_rice_crop_terms,
@@ -154,7 +157,8 @@ def test_run(
154
157
  _mock_get_cover_crop_property_terms,
155
158
  _mock_new_measurement,
156
159
  subfolder,
157
- load_cycles
160
+ load_cycles,
161
+ should_run
158
162
  ):
159
163
  folder = f"{fixtures_folder}/{subfolder}"
160
164
 
@@ -162,20 +166,17 @@ def test_run(
162
166
  with open(f"{folder}/cycles.jsonld", encoding='utf-8') as f:
163
167
  return json.load(f)
164
168
 
165
- mock_find_term_property.side_effect = find_term_property_side_effect
166
169
  mock_related_cycles.return_value = load_cycles_from_file() if load_cycles else []
167
170
 
168
171
  with open(f"{folder}/site.jsonld", encoding='utf-8') as f:
169
172
  site = json.load(f)
170
173
 
171
- with open(f"{folder}/result.jsonld", encoding='utf-8') as f:
172
- expected = json.load(f)
174
+ should_run_tier_1, should_run_tier_2, *_ = _should_run(site)
175
+ should_run_ = should_run_tier_1 or should_run_tier_2
173
176
 
174
- result = run(site)
175
- assert result == expected
177
+ assert should_run_ == should_run
176
178
 
177
179
 
178
- @patch(f"{class_path}._new_measurement", side_effect=fake_new_measurement)
179
180
  @patch(f"{class_path}.get_cover_crop_property_terms", return_value=COVER_CROP_PROPERTY_TERM_IDS)
180
181
  @patch(f"{class_path}.get_crop_residue_incorporated_or_left_on_field_terms", return_value=CROP_RESIDUE_INCORP_TERM_IDS)
181
182
  @patch(f"{class_path}.get_irrigated_terms", return_value=IRRIGATED_TERM_IDS)
@@ -183,11 +184,66 @@ def test_run(
183
184
  @patch(f"{class_path}.get_upland_rice_land_cover_terms", return_value=UPLAND_RICE_LAND_COVER_TERM_IDS)
184
185
  @patch(f"{class_path}.get_upland_rice_crop_terms", return_value=UPLAND_RICE_CROP_TERM_IDS)
185
186
  @patch(f"{class_path}.related_cycles", return_value=[])
186
- def test_run_no_data(*args):
187
+ @patch("hestia_earth.models.utils.property.find_term_property", side_effect=find_term_property_side_effect)
188
+ def test_should_run_no_data(*args):
187
189
  SITE = {}
188
- EXPECTED = []
189
- result = run(SITE)
190
- assert result == EXPECTED
190
+ EXPECTED = False
191
+
192
+ should_run_tier_1, should_run_tier_2, *_ = _should_run(SITE)
193
+ should_run = should_run_tier_1 or should_run_tier_2
194
+
195
+ assert should_run == EXPECTED
196
+
197
+
198
+ RUN_SUBFOLDERS = [
199
+ (subfolder, load_cycles) for subfolder, load_cycles, should_run in SHOULD_RUN_SUBFOLDERS
200
+ if should_run
201
+ ]
202
+
203
+
204
+ @mark.parametrize(
205
+ "subfolder, load_cycles",
206
+ RUN_SUBFOLDERS,
207
+ ids=[params[0] for params in RUN_SUBFOLDERS]
208
+ )
209
+ @patch(f"{class_path}._new_measurement", side_effect=fake_new_measurement)
210
+ @patch(f"{class_path}.get_cover_crop_property_terms", return_value=COVER_CROP_PROPERTY_TERM_IDS)
211
+ @patch(f"{class_path}.get_crop_residue_incorporated_or_left_on_field_terms", return_value=CROP_RESIDUE_INCORP_TERM_IDS)
212
+ @patch(f"{class_path}.get_irrigated_terms", return_value=IRRIGATED_TERM_IDS)
213
+ @patch(f"{class_path}.get_residue_removed_or_burnt_terms", return_value=RESIDUE_REMOVED_OR_BURNT_TERM_IDS)
214
+ @patch(f"{class_path}.get_upland_rice_land_cover_terms", return_value=UPLAND_RICE_LAND_COVER_TERM_IDS)
215
+ @patch(f"{class_path}.get_upland_rice_crop_terms", return_value=UPLAND_RICE_CROP_TERM_IDS)
216
+ @patch(f"{class_path}.related_cycles")
217
+ @patch("hestia_earth.models.utils.property.find_term_property", side_effect=find_term_property_side_effect)
218
+ def test_run(
219
+ mock_find_term_property,
220
+ mock_related_cycles,
221
+ _mock_get_upland_rice_crop_terms,
222
+ _mock_get_upland_rice_land_cover_terms,
223
+ _mock_get_residue_removed_or_burnt_terms,
224
+ _mock_get_irrigated_terms,
225
+ _mock_get_crop_residue_incorporated_or_left_on_field_terms,
226
+ _mock_get_cover_crop_property_terms,
227
+ _mock_new_measurement,
228
+ subfolder,
229
+ load_cycles
230
+ ):
231
+ folder = f"{fixtures_folder}/{subfolder}"
232
+
233
+ def load_cycles_from_file():
234
+ with open(f"{folder}/cycles.jsonld", encoding='utf-8') as f:
235
+ return json.load(f)
236
+
237
+ mock_related_cycles.return_value = load_cycles_from_file() if load_cycles else []
238
+
239
+ with open(f"{folder}/site.jsonld", encoding='utf-8') as f:
240
+ site = json.load(f)
241
+
242
+ with open(f"{folder}/result.jsonld", encoding='utf-8') as f:
243
+ expected = json.load(f)
244
+
245
+ result = run(site)
246
+ assert result == expected
191
247
 
192
248
 
193
249
  # --- TIER 2 TESTS: SUB-MODELS ---
@@ -227,6 +283,13 @@ def test_calc_water_factor():
227
283
  assert _calc_water_factor(1, 1) == _calc_water_factor(1000, 1000)
228
284
 
229
285
 
286
+ def test_calc_water_factor_zero():
287
+ """
288
+ Closes issue 771. Function should not raise 0 error and should return the maximum water factor.
289
+ """
290
+ assert _calc_water_factor(0, 0) == 1.49961875
291
+
292
+
230
293
  # --- IPCC SOIL CATEGORY TESTS ---
231
294
 
232
295
 
@@ -0,0 +1,45 @@
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.schmidt2007.h2SToAirWasteTreatment 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}.get_waste_values")
12
+ def test_should_run(mock_get_waste_values):
13
+ # no fuel values => no run
14
+ mock_get_waste_values.return_value = []
15
+ should_run, *args = _should_run({})
16
+ assert not should_run
17
+
18
+ # with fuel values => run
19
+ mock_get_waste_values.return_value = [0]
20
+ should_run, *args = _should_run({})
21
+ assert should_run is True
22
+
23
+
24
+ @patch(f"{class_path}._new_emission", side_effect=fake_new_emission)
25
+ def test_run(*args):
26
+ with open(f"{fixtures_folder}/cycle.jsonld", encoding='utf-8') as f:
27
+ cycle = json.load(f)
28
+
29
+ with open(f"{fixtures_folder}/result.jsonld", encoding='utf-8') as f:
30
+ expected = json.load(f)
31
+
32
+ value = run(cycle)
33
+ assert value == expected
34
+
35
+
36
+ @patch(f"{class_path}._new_emission", side_effect=fake_new_emission)
37
+ def test_run_data_complete(*args):
38
+ with open(f"{fixtures_folder}/no-input-data-complete/cycle.jsonld", encoding='utf-8') as f:
39
+ cycle = json.load(f)
40
+
41
+ with open(f"{fixtures_folder}/no-input-data-complete/result.jsonld", encoding='utf-8') as f:
42
+ expected = json.load(f)
43
+
44
+ value = run(cycle)
45
+ assert value == expected
@@ -1,8 +1,10 @@
1
1
  from datetime import datetime
2
2
  import pytest
3
+ from pytest import mark
3
4
  from unittest.mock import patch
4
5
 
5
6
  from hestia_earth.schema import SiteSiteType
7
+ from hestia_earth.utils.tools import parse
6
8
 
7
9
  from hestia_earth.models.utils.blank_node import (
8
10
  _calc_datetime_range_intersection_duration,
@@ -552,13 +554,31 @@ GROUP_NODES_BY_YEAR_SCENARIO_A_CYCLES = [
552
554
  ]
553
555
 
554
556
 
555
- def test_group_nodes_by_year_scenario_a():
557
+ @mark.parametrize(
558
+ "system_datetime",
559
+ [
560
+ datetime(2024, 5, 1), # Closes issue 772
561
+ datetime(2024, 1, 1), # Jan 1st
562
+ datetime(2024, 2, 29), # Leap year
563
+ datetime(2024, 12, 31), # Dec 31st
564
+ # 3 random dates...
565
+ datetime(2030, 5, 4),
566
+ datetime(2035, 7, 22),
567
+ datetime(2047, 8, 14)
568
+ ],
569
+ ids=lambda system_datetime: f"system_datetime = {datetime.strftime(system_datetime, r'%Y-%m-%d')}"
570
+ )
571
+ @patch("hestia_earth.utils.tools.parse")
572
+ def test_group_nodes_by_year_scenario_a(mock_parse, system_datetime):
556
573
  """
557
574
  Datestr in format `YYYY`. Some nodes missing `startDate` field. One multi-year cycle.
558
575
 
559
576
  Incomplete datestrs are gapfilled by default; with `endDate` datestrs snapping to the end
560
577
  of the calendar year, and `startDate` datestrs snapping to the beginning of the calendar
561
578
  year.
579
+
580
+ A bug used to occur in this function when it was run on the first day of a calendar month (e.g., 2024-04-01). To
581
+ verify that the bug is fixed we have simulated running the function on a variety of different dates.
562
582
  """
563
583
 
564
584
  EXPECTED = {
@@ -597,6 +617,9 @@ def test_group_nodes_by_year_scenario_a():
597
617
  }]
598
618
  }
599
619
 
620
+ # As we can't mock builtin method datetime.now(), we have to pass our test system_datetime into the parse function.
621
+ mock_parse.side_effect = lambda *args, **kwargs: parse(*args, default=system_datetime, **kwargs)
622
+
600
623
  result = group_nodes_by_year(GROUP_NODES_BY_YEAR_SCENARIO_A_CYCLES)
601
624
  assert result == EXPECTED
602
625
 
@@ -965,7 +988,26 @@ def test_group_nodes_by_year_missing_dates():
965
988
  assert result == EXPECTED
966
989
 
967
990
 
968
- def test_group_nodes_by_year_multiple_values_and_dates():
991
+ @mark.parametrize(
992
+ "system_datetime",
993
+ [
994
+ datetime(2024, 5, 1), # Closes issue 772
995
+ datetime(2024, 1, 1), # Jan 1st
996
+ datetime(2024, 2, 29), # Leap year
997
+ datetime(2024, 12, 31), # Dec 31st
998
+ # 3 random dates...
999
+ datetime(2026, 5, 14),
1000
+ datetime(2032, 10, 10),
1001
+ datetime(2043, 8, 22)
1002
+ ],
1003
+ ids=lambda system_datetime: f"system_datetime = {datetime.strftime(system_datetime, r'%Y-%m-%d')}"
1004
+ )
1005
+ @patch("hestia_earth.utils.tools.parse")
1006
+ def test_group_nodes_by_year_multiple_values_and_dates(mock_parse, system_datetime):
1007
+ """
1008
+ A bug used to occur in this function when it was run on the first day of a calendar month (e.g., 2024-04-01). To
1009
+ verify that the bug is fixed we have simulated running the function on a variety of different dates.
1010
+ """
969
1011
 
970
1012
  NODES = [{
971
1013
  "value": [1, 2, 3, 4, 5],
@@ -1005,11 +1047,34 @@ def test_group_nodes_by_year_multiple_values_and_dates():
1005
1047
  }]
1006
1048
  }
1007
1049
 
1050
+ # As we can't mock builtin method datetime.now(), we have to pass our test system_datetime into the parse function.
1051
+ mock_parse.side_effect = lambda *args, **kwargs: parse(*args, default=system_datetime, **kwargs)
1052
+
1008
1053
  result = group_nodes_by_year(NODES, mode=GroupNodesByYearMode.DATES)
1009
1054
  assert result == EXPECTED
1010
1055
 
1011
1056
 
1012
- def test_group_nodes_by_year_and_month():
1057
+ @mark.parametrize(
1058
+ "system_datetime",
1059
+ [
1060
+ datetime(2024, 5, 1), # Closes issue 772
1061
+ datetime(2024, 1, 1), # Jan 1st
1062
+ datetime(2024, 2, 29), # Leap year
1063
+ datetime(2024, 12, 31), # Dec 31st
1064
+ # 3 random dates...
1065
+ datetime(2026, 4, 6),
1066
+ datetime(2034, 7, 24),
1067
+ datetime(2049, 10, 10)
1068
+ ],
1069
+ ids=lambda system_datetime: f"system_datetime = {datetime.strftime(system_datetime, r'%Y-%m-%d')}"
1070
+ )
1071
+ @patch("hestia_earth.utils.tools.parse")
1072
+ def test_group_nodes_by_year_and_month(mock_parse, system_datetime):
1073
+ """
1074
+ A bug used to occur in this function when it was run on the first day of a calendar month (e.g., 2024-04-01). To
1075
+ verify that the bug is fixed we have simulated running the function on a variety of different dates.
1076
+ """
1077
+
1013
1078
  MANAGEMENT = [
1014
1079
  {
1015
1080
  "term.@id": "fullTillage",
@@ -1064,5 +1129,8 @@ def test_group_nodes_by_year_and_month():
1064
1129
  }
1065
1130
  }
1066
1131
 
1132
+ # As we can't mock builtin method datetime.now(), we have to pass our test system date into the parse function.
1133
+ mock_parse.side_effect = lambda *args, **kwargs: parse(*args, default=system_datetime, **kwargs)
1134
+
1067
1135
  result = group_nodes_by_year_and_month(MANAGEMENT)
1068
1136
  assert result == EXPECTED