hestia-earth-models 0.70.1__py3-none-any.whl → 0.70.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hestia_earth/models/cml2001Baseline/resourceUseMineralsAndMetalsDuringCycle.py +2 -1
- hestia_earth/models/config/Cycle.json +68 -0
- hestia_earth/models/config/Site.json +8 -0
- hestia_earth/models/cycle/practice/landCover.py +181 -0
- hestia_earth/models/emepEea2019/nh3ToAirExcreta.py +1 -1
- hestia_earth/models/hestia/excretaKgMass.py +1 -1
- hestia_earth/models/hestia/management.py +15 -113
- hestia_earth/models/hestia/pToSurfaceWaterAquacultureSystems.py +148 -0
- hestia_earth/models/hestia/soilMeasurement.py +1 -1
- hestia_earth/models/ipcc2019/ch4ToAirFloodedRice.py +8 -6
- hestia_earth/models/ipcc2019/ch4ToAirOrganicSoilCultivation.py +270 -0
- hestia_earth/models/ipcc2019/co2ToAirAboveGroundBiomassStockChange.py +0 -3
- hestia_earth/models/ipcc2019/co2ToAirBelowGroundBiomassStockChange.py +0 -3
- hestia_earth/models/ipcc2019/co2ToAirCarbonStockChange_utils.py +57 -43
- hestia_earth/models/ipcc2019/co2ToAirLimeHydrolysis.py +7 -5
- hestia_earth/models/ipcc2019/co2ToAirOrganicSoilCultivation.py +215 -0
- hestia_earth/models/ipcc2019/co2ToAirSoilOrganicCarbonStockChange.py +0 -3
- hestia_earth/models/ipcc2019/n2OToAirOrganicSoilCultivationDirect.py +161 -0
- hestia_earth/models/ipcc2019/no3ToGroundwaterExcreta.py +1 -1
- hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_2.py +15 -4
- hestia_earth/models/ipcc2019/organicSoilCultivation_utils.py +159 -0
- hestia_earth/models/mocking/search-results.json +713 -705
- hestia_earth/models/pooreNemecek2018/excretaKgN.py +3 -1
- hestia_earth/models/site/grouped_measurement.py +132 -0
- hestia_earth/models/utils/__init__.py +4 -3
- hestia_earth/models/utils/blank_node.py +40 -11
- hestia_earth/models/utils/constant.py +26 -20
- hestia_earth/models/utils/excretaManagement.py +2 -2
- hestia_earth/models/utils/product.py +39 -1
- hestia_earth/models/utils/property.py +25 -12
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.70.1.dist-info → hestia_earth_models-0.70.3.dist-info}/METADATA +2 -2
- {hestia_earth_models-0.70.1.dist-info → hestia_earth_models-0.70.3.dist-info}/RECORD +49 -36
- tests/models/cycle/practice/test_landCover.py +27 -0
- tests/models/hestia/test_feedConversionRatio.py +2 -3
- tests/models/hestia/test_pToSurfaceWaterAquacultureSystems.py +56 -0
- tests/models/hestia/test_soilMeasurement.py +11 -19
- tests/models/ipcc2019/test_ch4ToAirEntericFermentation.py +4 -7
- tests/models/ipcc2019/test_ch4ToAirOrganicSoilCultivation.py +61 -0
- tests/models/ipcc2019/test_co2ToAirAboveGroundBiomassStockChange.py +11 -9
- tests/models/ipcc2019/test_co2ToAirBelowGroundBiomassStockChange.py +10 -8
- tests/models/ipcc2019/test_co2ToAirLimeHydrolysis.py +1 -1
- tests/models/ipcc2019/test_co2ToAirOrganicSoilCultivation.py +62 -0
- tests/models/ipcc2019/test_co2ToAirSoilOrganicCarbonStockChange.py +11 -9
- tests/models/ipcc2019/test_n2OToAirOrganicSoilCultivationDirect.py +61 -0
- tests/models/site/test_grouped_measurement.py +20 -0
- {hestia_earth_models-0.70.1.dist-info → hestia_earth_models-0.70.3.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.70.1.dist-info → hestia_earth_models-0.70.3.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.70.1.dist-info → hestia_earth_models-0.70.3.dist-info}/top_level.txt +0 -0
@@ -6,7 +6,8 @@ from hestia_earth.utils.model import filter_list_term_type
|
|
6
6
|
from hestia_earth.utils.tools import list_sum
|
7
7
|
|
8
8
|
from hestia_earth.models.log import logRequirements, logShouldRun
|
9
|
-
from hestia_earth.models.utils import _filter_list_term_unit
|
9
|
+
from hestia_earth.models.utils import _filter_list_term_unit
|
10
|
+
from hestia_earth.models.utils.constant import Units
|
10
11
|
from hestia_earth.models.utils.blank_node import _sum_nodes_value
|
11
12
|
from hestia_earth.models.utils.indicator import _new_indicator
|
12
13
|
from . import MODEL
|
@@ -182,6 +182,14 @@
|
|
182
182
|
"mergeStrategy": "list",
|
183
183
|
"stage": 1
|
184
184
|
},
|
185
|
+
{
|
186
|
+
"key": "practices",
|
187
|
+
"model": "cycle",
|
188
|
+
"value": "practice.landCover",
|
189
|
+
"runStrategy": "always",
|
190
|
+
"mergeStrategy": "list",
|
191
|
+
"stage": 1
|
192
|
+
},
|
185
193
|
{
|
186
194
|
"key": "products",
|
187
195
|
"model": "hestia",
|
@@ -1759,6 +1767,51 @@
|
|
1759
1767
|
},
|
1760
1768
|
"stage": 2
|
1761
1769
|
},
|
1770
|
+
{
|
1771
|
+
"key": "emissions",
|
1772
|
+
"model": "ipcc2019",
|
1773
|
+
"value": "ch4ToAirOrganicSoilCultivation",
|
1774
|
+
"runStrategy": "add_blank_node_if_missing",
|
1775
|
+
"runArgs": {
|
1776
|
+
"runNonMeasured": true,
|
1777
|
+
"runNonAddedTerm": true
|
1778
|
+
},
|
1779
|
+
"mergeStrategy": "list",
|
1780
|
+
"mergeArgs": {
|
1781
|
+
"replaceThreshold": ["value", 0.01]
|
1782
|
+
},
|
1783
|
+
"stage": 2
|
1784
|
+
},
|
1785
|
+
{
|
1786
|
+
"key": "emissions",
|
1787
|
+
"model": "ipcc2019",
|
1788
|
+
"value": "co2ToAirOrganicSoilCultivation",
|
1789
|
+
"runStrategy": "add_blank_node_if_missing",
|
1790
|
+
"runArgs": {
|
1791
|
+
"runNonMeasured": true,
|
1792
|
+
"runNonAddedTerm": true
|
1793
|
+
},
|
1794
|
+
"mergeStrategy": "list",
|
1795
|
+
"mergeArgs": {
|
1796
|
+
"replaceThreshold": ["value", 0.01]
|
1797
|
+
},
|
1798
|
+
"stage": 2
|
1799
|
+
},
|
1800
|
+
{
|
1801
|
+
"key": "emissions",
|
1802
|
+
"model": "ipcc2019",
|
1803
|
+
"value": "n2OToAirOrganicSoilCultivationDirect",
|
1804
|
+
"runStrategy": "add_blank_node_if_missing",
|
1805
|
+
"runArgs": {
|
1806
|
+
"runNonMeasured": true,
|
1807
|
+
"runNonAddedTerm": true
|
1808
|
+
},
|
1809
|
+
"mergeStrategy": "list",
|
1810
|
+
"mergeArgs": {
|
1811
|
+
"replaceThreshold": ["value", 0.01]
|
1812
|
+
},
|
1813
|
+
"stage": 2
|
1814
|
+
},
|
1762
1815
|
{
|
1763
1816
|
"key": "emissions",
|
1764
1817
|
"model": "ipcc2019",
|
@@ -2178,6 +2231,21 @@
|
|
2178
2231
|
"replaceThreshold": ["value", 0.01]
|
2179
2232
|
},
|
2180
2233
|
"stage": 2
|
2234
|
+
},
|
2235
|
+
{
|
2236
|
+
"key": "emissions",
|
2237
|
+
"model": "hestia",
|
2238
|
+
"value": "pToSurfaceWaterAquacultureSystems",
|
2239
|
+
"runStrategy": "add_blank_node_if_missing",
|
2240
|
+
"runArgs": {
|
2241
|
+
"runNonMeasured": true,
|
2242
|
+
"runNonAddedTerm": true
|
2243
|
+
},
|
2244
|
+
"mergeStrategy": "list",
|
2245
|
+
"mergeArgs": {
|
2246
|
+
"replaceThreshold": ["value", 0.01]
|
2247
|
+
},
|
2248
|
+
"stage": 2
|
2181
2249
|
}
|
2182
2250
|
],
|
2183
2251
|
{
|
@@ -400,6 +400,14 @@
|
|
400
400
|
"mergeStrategy": "list",
|
401
401
|
"stage": 1
|
402
402
|
},
|
403
|
+
{
|
404
|
+
"key": "measurements",
|
405
|
+
"model": "site",
|
406
|
+
"value": "grouped_measurement",
|
407
|
+
"runStrategy": "always",
|
408
|
+
"mergeStrategy": "list",
|
409
|
+
"stage": 1
|
410
|
+
},
|
403
411
|
{
|
404
412
|
"key": "measurements",
|
405
413
|
"model": "hestia",
|
@@ -0,0 +1,181 @@
|
|
1
|
+
from functools import reduce
|
2
|
+
from hestia_earth.schema import TermTermType, SiteSiteType
|
3
|
+
from hestia_earth.utils.model import filter_list_term_type
|
4
|
+
from hestia_earth.utils.tools import non_empty_list, flatten, list_sum
|
5
|
+
|
6
|
+
from hestia_earth.models.log import logRequirements, logShouldRun
|
7
|
+
from hestia_earth.models.utils import _omit, _include
|
8
|
+
from hestia_earth.models.utils.practice import _new_practice
|
9
|
+
from hestia_earth.models.utils.term import get_lookup_value
|
10
|
+
from hestia_earth.models.utils.blank_node import condense_nodes
|
11
|
+
from hestia_earth.models.utils.crop import get_landCover_term_id
|
12
|
+
from .. import MODEL
|
13
|
+
|
14
|
+
REQUIREMENTS = {
|
15
|
+
"Cycle": {
|
16
|
+
"endDate": "",
|
17
|
+
"products": [
|
18
|
+
{
|
19
|
+
"@type": "Product",
|
20
|
+
"term.termType": ["crop", "forage"],
|
21
|
+
"optional": {
|
22
|
+
"startDate": "",
|
23
|
+
"endDate": ""
|
24
|
+
}
|
25
|
+
}
|
26
|
+
],
|
27
|
+
"site": {
|
28
|
+
"@type": "Site",
|
29
|
+
"siteType": "cropland"
|
30
|
+
},
|
31
|
+
"none": {
|
32
|
+
"practices": [{"@type": "Practice", "term.termType": "landCover"}]
|
33
|
+
},
|
34
|
+
"optional": {
|
35
|
+
"startDate": ""
|
36
|
+
}
|
37
|
+
}
|
38
|
+
}
|
39
|
+
RETURNS = {
|
40
|
+
"Practice": [{
|
41
|
+
"term.termType": "landCover",
|
42
|
+
"value": "",
|
43
|
+
"endDate": "",
|
44
|
+
"startDate": ""
|
45
|
+
}]
|
46
|
+
}
|
47
|
+
LOOKUPS = {
|
48
|
+
"crop": ["landCoverTermId", "maximumCycleDuration"],
|
49
|
+
"forage": ["landCoverTermId"],
|
50
|
+
"property": ["GAP_FILL_TO_MANAGEMENT", "CALCULATE_TOTAL_LAND_COVER_SHARE_SEPARATELY"]
|
51
|
+
}
|
52
|
+
MODEL_KEY = 'landCover'
|
53
|
+
|
54
|
+
|
55
|
+
def practice(data: dict):
|
56
|
+
node = _new_practice(data.get('id'))
|
57
|
+
node['value'] = [data['value']]
|
58
|
+
node['endDate'] = data['endDate']
|
59
|
+
if data.get('startDate'):
|
60
|
+
node['startDate'] = data['startDate']
|
61
|
+
if data.get('properties'):
|
62
|
+
node['properties'] = data['properties']
|
63
|
+
return node
|
64
|
+
|
65
|
+
|
66
|
+
def _should_gap_fill(term: dict):
|
67
|
+
value = get_lookup_value(lookup_term=term, column='GAP_FILL_TO_MANAGEMENT')
|
68
|
+
return bool(value)
|
69
|
+
|
70
|
+
|
71
|
+
def _filter_properties(blank_node: dict):
|
72
|
+
properties = list(filter(lambda p: _should_gap_fill(p.get('term', {})), blank_node.get('properties', [])))
|
73
|
+
return _omit(blank_node, ['properties']) | ({'properties': properties} if properties else {})
|
74
|
+
|
75
|
+
|
76
|
+
def _map_to_value(value: dict):
|
77
|
+
return {
|
78
|
+
'id': value.get('term', {}).get('@id'),
|
79
|
+
'value': value.get('value'),
|
80
|
+
'startDate': value.get('startDate'),
|
81
|
+
'endDate': value.get('endDate'),
|
82
|
+
'properties': value.get('properties')
|
83
|
+
}
|
84
|
+
|
85
|
+
|
86
|
+
def _copy_item_if_exists(source: dict, keys: list[str] = None, dest: dict = None) -> dict:
|
87
|
+
return reduce(lambda p, c: p | ({c: source[c]} if source.get(c) else {}), keys or [], dest or {})
|
88
|
+
|
89
|
+
|
90
|
+
def _run(cycle: dict, products: list, total: float):
|
91
|
+
# remove any properties that should not get gap-filled
|
92
|
+
products = list(map(_filter_properties, products))
|
93
|
+
|
94
|
+
nodes = [
|
95
|
+
_map_to_value(_include(cycle, ["startDate", "endDate"]) | _copy_item_if_exists(
|
96
|
+
source=product,
|
97
|
+
keys=['properties', 'startDate', 'endDate'],
|
98
|
+
dest={
|
99
|
+
"term": {'@id': product.get('land-cover-id')},
|
100
|
+
"value": round((100 - total) / len(products), 2)
|
101
|
+
}
|
102
|
+
))
|
103
|
+
for product in products
|
104
|
+
]
|
105
|
+
|
106
|
+
return condense_nodes(list(map(practice, nodes)))
|
107
|
+
|
108
|
+
|
109
|
+
def _should_group_landCover(term: dict):
|
110
|
+
value = get_lookup_value(lookup_term=term, column='CALCULATE_TOTAL_LAND_COVER_SHARE_SEPARATELY')
|
111
|
+
return bool(value)
|
112
|
+
|
113
|
+
|
114
|
+
def _has_prop_grouped_with_landCover(product: dict):
|
115
|
+
return bool(
|
116
|
+
next((
|
117
|
+
p
|
118
|
+
for p in product.get('properties', [])
|
119
|
+
if _should_group_landCover(p.get('term', {}))
|
120
|
+
), None)
|
121
|
+
)
|
122
|
+
|
123
|
+
|
124
|
+
def _product_wit_landCover_id(product: dict):
|
125
|
+
landCover_id = get_landCover_term_id(product.get('term', {}))
|
126
|
+
return product | {'land-cover-id': landCover_id} if landCover_id else None
|
127
|
+
|
128
|
+
|
129
|
+
def _should_run(cycle: dict):
|
130
|
+
is_cropland = cycle.get('site', {}).get('siteType') == SiteSiteType.CROPLAND.value
|
131
|
+
|
132
|
+
practices = filter_list_term_type(cycle.get('practices', []), TermTermType.LANDCOVER)
|
133
|
+
# split practices with properties that group with landCover
|
134
|
+
practices_max_100 = [
|
135
|
+
p for p in practices if _has_prop_grouped_with_landCover(p)
|
136
|
+
]
|
137
|
+
total_practices_max_100 = list_sum([
|
138
|
+
list_sum(p.get('value', []))
|
139
|
+
for p in practices_max_100
|
140
|
+
])
|
141
|
+
practices_without_grouped_props = [
|
142
|
+
p for p in practices if not _has_prop_grouped_with_landCover(p)
|
143
|
+
]
|
144
|
+
|
145
|
+
products = filter_list_term_type(cycle.get('products', []), [TermTermType.CROP, TermTermType.FORAGE])
|
146
|
+
# only take products with a matching landCover term
|
147
|
+
products = non_empty_list(map(_product_wit_landCover_id, products))
|
148
|
+
|
149
|
+
# Products that can sum up to 100% => run if total is below 100%
|
150
|
+
products_max_100 = [
|
151
|
+
p for p in products
|
152
|
+
if _has_prop_grouped_with_landCover(p)
|
153
|
+
] if total_practices_max_100 < 100 else []
|
154
|
+
|
155
|
+
# Products that must sum up to 100% => can not run practices already exist as already 100%
|
156
|
+
products_is_100 = [
|
157
|
+
p for p in products
|
158
|
+
if not _has_prop_grouped_with_landCover(p)
|
159
|
+
] if not practices_without_grouped_props else []
|
160
|
+
|
161
|
+
has_crop_forage_products = bool(products_max_100 + products_is_100)
|
162
|
+
|
163
|
+
logRequirements(cycle, model=MODEL, model_key=MODEL_KEY,
|
164
|
+
is_cropland=is_cropland,
|
165
|
+
has_crop_forage_products=has_crop_forage_products)
|
166
|
+
|
167
|
+
should_run = all([is_cropland, has_crop_forage_products])
|
168
|
+
logShouldRun(cycle, MODEL, None, should_run, model_key=MODEL_KEY)
|
169
|
+
|
170
|
+
return should_run, [
|
171
|
+
(products_max_100, total_practices_max_100),
|
172
|
+
(products_is_100, 0)
|
173
|
+
]
|
174
|
+
|
175
|
+
|
176
|
+
def run(cycle: dict):
|
177
|
+
should_run, products_list = _should_run(cycle)
|
178
|
+
return flatten([
|
179
|
+
_run(cycle, products, total)
|
180
|
+
for products, total in products_list if products
|
181
|
+
]) if should_run else []
|
@@ -46,7 +46,7 @@ def _run(excreta_EF_input: float):
|
|
46
46
|
def _should_run(cycle: dict):
|
47
47
|
excreta_complete = _is_term_type_complete(cycle, TermTermType.EXCRETA)
|
48
48
|
excreta_EF_input = get_excreta_inputs_with_factor(
|
49
|
-
cycle, f"{list(LOOKUPS.keys())[0]}.csv",
|
49
|
+
cycle, f"{list(LOOKUPS.keys())[0]}.csv", excreta_conversion_func=total_excreta_tan, model=MODEL, term=TERM_ID
|
50
50
|
)
|
51
51
|
|
52
52
|
logRequirements(cycle, model=MODEL, term=TERM_ID,
|
@@ -1,6 +1,5 @@
|
|
1
1
|
from typing import List
|
2
2
|
from datetime import timedelta, datetime
|
3
|
-
from functools import reduce
|
4
3
|
from hestia_earth.schema import SchemaType, TermTermType, SiteSiteType, COMPLETENESS_MAPPING
|
5
4
|
from hestia_earth.utils.lookup import column_name, get_table_value, download_lookup
|
6
5
|
from hestia_earth.utils.model import filter_list_term_type
|
@@ -8,7 +7,7 @@ from hestia_earth.utils.tools import safe_parse_float, flatten
|
|
8
7
|
from hestia_earth.utils.blank_node import get_node_value
|
9
8
|
|
10
9
|
from hestia_earth.models.log import logRequirements, logShouldRun, log_as_table
|
11
|
-
from hestia_earth.models.utils import _include,
|
10
|
+
from hestia_earth.models.utils import _include, group_by
|
12
11
|
from hestia_earth.models.utils.management import _new_management
|
13
12
|
from hestia_earth.models.utils.term import get_lookup_value
|
14
13
|
from hestia_earth.models.utils.blank_node import condense_nodes, DatestrFormat, _gapfill_datestr, DatestrGapfillMode
|
@@ -24,20 +23,16 @@ REQUIREMENTS = {
|
|
24
23
|
"Cycle": [{
|
25
24
|
"@type": "Cycle",
|
26
25
|
"endDate": "",
|
27
|
-
"products": [
|
28
|
-
{
|
29
|
-
"@type": "Product",
|
30
|
-
"term.termType": ["crop", "forage", "landCover"]
|
31
|
-
}
|
32
|
-
],
|
33
26
|
"practices": [
|
34
27
|
{
|
28
|
+
"@type": "Practice",
|
35
29
|
"term.termType": [
|
36
30
|
"waterRegime",
|
37
31
|
"tillage",
|
38
32
|
"cropResidueManagement",
|
39
33
|
"landUseManagement",
|
40
|
-
"system"
|
34
|
+
"system",
|
35
|
+
"landCover"
|
41
36
|
],
|
42
37
|
"value": ""
|
43
38
|
}
|
@@ -62,7 +57,6 @@ REQUIREMENTS = {
|
|
62
57
|
}
|
63
58
|
RETURNS = {
|
64
59
|
"Management": [{
|
65
|
-
"@type": "Management",
|
66
60
|
"term.termType": [
|
67
61
|
"landCover", "waterRegime", "tillage", "cropResidueManagement", "landUseManagement", "system"
|
68
62
|
],
|
@@ -78,7 +72,7 @@ LOOKUPS = {
|
|
78
72
|
"organicFertiliser": "ANIMAL_MANURE",
|
79
73
|
"soilAmendment": "PRACTICE_INCREASING_C_INPUT",
|
80
74
|
"landUseManagement": "GAP_FILL_TO_MANAGEMENT",
|
81
|
-
"property":
|
75
|
+
"property": "GAP_FILL_TO_MANAGEMENT"
|
82
76
|
}
|
83
77
|
MODEL_KEY = 'management'
|
84
78
|
|
@@ -87,7 +81,8 @@ _PRACTICES_TERM_TYPES = [
|
|
87
81
|
TermTermType.TILLAGE,
|
88
82
|
TermTermType.CROPRESIDUEMANAGEMENT,
|
89
83
|
TermTermType.LANDUSEMANAGEMENT,
|
90
|
-
TermTermType.SYSTEM
|
84
|
+
TermTermType.SYSTEM,
|
85
|
+
TermTermType.LANDCOVER
|
91
86
|
]
|
92
87
|
_PRACTICES_COMPLETENESS_MAPPING = COMPLETENESS_MAPPING.get(SchemaType.PRACTICE.value, {})
|
93
88
|
_ANIMAL_MANURE_USED_TERM_ID = "animalManureUsed"
|
@@ -125,7 +120,6 @@ _INPUT_RULES = {
|
|
125
120
|
_SKIP_LAND_COVER_SITE_TYPES = [
|
126
121
|
SiteSiteType.CROPLAND.value
|
127
122
|
]
|
128
|
-
_CYCLE_DATE_TERM_TYPES = {TermTermType.CROP.value, TermTermType.FORAGE.value}
|
129
123
|
|
130
124
|
|
131
125
|
def management(data: dict):
|
@@ -147,7 +141,7 @@ def _get_cycle_duration(cycle: dict, land_cover_id: str):
|
|
147
141
|
land_cover_id,
|
148
142
|
column_name('maximumCycleDuration')
|
149
143
|
), default=None)
|
150
|
-
return cycle_duration or
|
144
|
+
return cycle_duration or lookup_value
|
151
145
|
|
152
146
|
|
153
147
|
def _gap_filled_date_only_str(date_str: str, mode: str = DatestrGapfillMode.END) -> str:
|
@@ -165,11 +159,9 @@ def _gap_filled_start_date(land_cover_id: str, end_date: str, cycle: dict) -> di
|
|
165
159
|
"""If possible, gap-fill the startDate based on the endDate - cycleDuration"""
|
166
160
|
cycle_duration = _get_cycle_duration(cycle, land_cover_id)
|
167
161
|
return {
|
168
|
-
"startDate":
|
169
|
-
_gap_filled_date_obj(
|
170
|
-
|
171
|
-
_gap_filled_date_obj(cycle.get("startDate"), mode=DatestrGapfillMode.START)
|
172
|
-
if cycle.get("startDate") else datetime.fromtimestamp(0)
|
162
|
+
"startDate": (
|
163
|
+
_gap_filled_date_obj(cycle.get("startDate"), mode=DatestrGapfillMode.START) if cycle.get("startDate") else
|
164
|
+
_gap_filled_date_obj(end_date) - timedelta(days=cycle_duration - 1)
|
173
165
|
)
|
174
166
|
} if any([cycle_duration, cycle.get("startDate")]) else {}
|
175
167
|
|
@@ -190,11 +182,6 @@ def _should_gap_fill(term: dict):
|
|
190
182
|
return bool(value)
|
191
183
|
|
192
184
|
|
193
|
-
def _filter_properties(blank_node: dict):
|
194
|
-
properties = list(filter(lambda p: _should_gap_fill(p.get('term', {})), blank_node.get('properties', [])))
|
195
|
-
return _omit(blank_node, ['properties']) | ({'properties': properties} if properties else {})
|
196
|
-
|
197
|
-
|
198
185
|
def _map_to_value(value: dict):
|
199
186
|
return {
|
200
187
|
'id': value.get('term', {}).get('@id'),
|
@@ -209,10 +196,6 @@ def _extract_node_value(node: dict) -> dict:
|
|
209
196
|
return node | {'value': get_node_value(node)}
|
210
197
|
|
211
198
|
|
212
|
-
def _copy_item_if_exists(source: dict, keys: list[str] = None, dest: dict = None) -> dict:
|
213
|
-
return reduce(lambda p, c: p | ({c: source[c]} if source.get(c) else {}), keys or [], dest or {})
|
214
|
-
|
215
|
-
|
216
199
|
def _get_relevant_items(cycle: dict, item_name: str, term_types: List[TermTermType], completeness_mapping: dict = {}):
|
217
200
|
"""
|
218
201
|
Get items from the list of cycles with any of the relevant terms.
|
@@ -280,84 +263,6 @@ def _run_from_siteType(site: dict, cycle: dict):
|
|
280
263
|
}] if should_run else []
|
281
264
|
|
282
265
|
|
283
|
-
def _run_products(cycle: dict, products: list, total_products: int = None, use_cycle_dates: bool = False):
|
284
|
-
default_dates = _include_with_date_gap_fill(cycle, ["startDate", "endDate"])
|
285
|
-
return [
|
286
|
-
_map_to_value(default_dates | _copy_item_if_exists(
|
287
|
-
source=product,
|
288
|
-
keys=['properties', 'startDate', 'endDate'],
|
289
|
-
dest={
|
290
|
-
"term": {'@id': get_landCover_term_id(product.get('term', {}))},
|
291
|
-
"value": round(100 / (total_products or len(products)), 2)
|
292
|
-
}
|
293
|
-
) | (
|
294
|
-
default_dates if use_cycle_dates or product.get("term", {}).get("termType") in _CYCLE_DATE_TERM_TYPES
|
295
|
-
else {}
|
296
|
-
))
|
297
|
-
for product in products
|
298
|
-
]
|
299
|
-
|
300
|
-
|
301
|
-
def _run_from_landCover(cycle: dict, crop_forage_products: list):
|
302
|
-
"""
|
303
|
-
Copy landCover items, and include crop/forage landCover items with properties to count in ratio.
|
304
|
-
"""
|
305
|
-
land_cover_products = [
|
306
|
-
_map_to_value(_extract_node_value(
|
307
|
-
_include_with_date_gap_fill(
|
308
|
-
value=product,
|
309
|
-
keys=["term", "value", "startDate", "endDate", "properties"]
|
310
|
-
)
|
311
|
-
)) for product in _get_relevant_items(
|
312
|
-
cycle=cycle,
|
313
|
-
item_name="products",
|
314
|
-
term_types=[TermTermType.LANDCOVER]
|
315
|
-
)
|
316
|
-
]
|
317
|
-
return land_cover_products + _run_products(
|
318
|
-
cycle,
|
319
|
-
crop_forage_products,
|
320
|
-
total_products=len(crop_forage_products) + len(land_cover_products),
|
321
|
-
use_cycle_dates=True
|
322
|
-
)
|
323
|
-
|
324
|
-
|
325
|
-
def _should_group_landCover(term: dict):
|
326
|
-
value = get_lookup_value(lookup_term=term, column='CALCULATE_TOTAL_LAND_COVER_SHARE_SEPARATELY')
|
327
|
-
return bool(value)
|
328
|
-
|
329
|
-
|
330
|
-
def _has_prop_grouped_with_landCover(product: dict):
|
331
|
-
return bool(
|
332
|
-
next((
|
333
|
-
p
|
334
|
-
for p in product.get('properties', [])
|
335
|
-
if _should_group_landCover(p.get('term', {}))
|
336
|
-
), None)
|
337
|
-
)
|
338
|
-
|
339
|
-
|
340
|
-
def _run_from_crop_forage(cycle: dict, site: dict):
|
341
|
-
products = _get_relevant_items(
|
342
|
-
cycle=cycle,
|
343
|
-
item_name="products",
|
344
|
-
term_types=[TermTermType.CROP, TermTermType.FORAGE]
|
345
|
-
) if site.get("siteType", "") == SiteSiteType.CROPLAND.value else []
|
346
|
-
# only take products with a matching landCover term
|
347
|
-
products = [p for p in products if get_landCover_term_id(p.get('term', {}))]
|
348
|
-
# remove any properties that should not get gap-filled
|
349
|
-
products = list(map(_filter_properties, products))
|
350
|
-
|
351
|
-
# split products with properties that group with landCover
|
352
|
-
products_with_gap_filled_props = [p for p in products if _has_prop_grouped_with_landCover(p)]
|
353
|
-
products_without_gap_filled_props = [p for p in products if not _has_prop_grouped_with_landCover(p)]
|
354
|
-
|
355
|
-
return _run_from_landCover(
|
356
|
-
cycle=cycle,
|
357
|
-
crop_forage_products=products_with_gap_filled_props
|
358
|
-
) + _run_products(cycle, products_without_gap_filled_props, use_cycle_dates=False)
|
359
|
-
|
360
|
-
|
361
266
|
def _should_run_practice(practice: dict):
|
362
267
|
"""
|
363
268
|
Include only landUseManagement practices where GAP_FILL_TO_MANAGEMENT = True
|
@@ -371,7 +276,7 @@ def _run_from_practices(cycle: dict):
|
|
371
276
|
_extract_node_value(
|
372
277
|
_include_with_date_gap_fill(
|
373
278
|
value=practice,
|
374
|
-
keys=["term", "value", "startDate", "endDate"]
|
279
|
+
keys=["term", "value", "startDate", "endDate", "properties"]
|
375
280
|
)
|
376
281
|
) for practice in _get_relevant_items(
|
377
282
|
cycle=cycle,
|
@@ -380,18 +285,16 @@ def _run_from_practices(cycle: dict):
|
|
380
285
|
completeness_mapping=_PRACTICES_COMPLETENESS_MAPPING
|
381
286
|
)
|
382
287
|
]
|
383
|
-
|
384
|
-
return practices
|
288
|
+
return list(map(_map_to_value, filter(_should_run_practice, practices)))
|
385
289
|
|
386
290
|
|
387
291
|
def _run_cycle(site: dict, cycle: dict):
|
388
292
|
inputs = _run_from_inputs(site, cycle)
|
389
|
-
products = _run_from_crop_forage(site=site, cycle=cycle)
|
390
293
|
site_types = _run_from_siteType(site=site, cycle=cycle)
|
391
294
|
practices = _run_from_practices(cycle)
|
392
295
|
return [
|
393
296
|
node | {'cycle-id': cycle.get('@id')}
|
394
|
-
for node in inputs +
|
297
|
+
for node in inputs + site_types + practices
|
395
298
|
]
|
396
299
|
|
397
300
|
|
@@ -411,5 +314,4 @@ def run(site: dict):
|
|
411
314
|
)
|
412
315
|
logShouldRun(site, MODEL, id, True, model_key=MODEL_KEY)
|
413
316
|
|
414
|
-
|
415
|
-
return management_nodes
|
317
|
+
return condense_nodes(list(map(management, nodes)))
|