hestia-earth-models 0.70.1__py3-none-any.whl → 0.70.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.
Files changed (42) hide show
  1. hestia_earth/models/cml2001Baseline/resourceUseMineralsAndMetalsDuringCycle.py +2 -1
  2. hestia_earth/models/config/Cycle.json +60 -0
  3. hestia_earth/models/config/Site.json +8 -0
  4. hestia_earth/models/hestia/excretaKgMass.py +1 -1
  5. hestia_earth/models/hestia/management.py +4 -6
  6. hestia_earth/models/hestia/pToSurfaceWaterAquacultureSystems.py +148 -0
  7. hestia_earth/models/hestia/soilMeasurement.py +1 -1
  8. hestia_earth/models/ipcc2019/ch4ToAirFloodedRice.py +8 -6
  9. hestia_earth/models/ipcc2019/ch4ToAirOrganicSoilCultivation.py +270 -0
  10. hestia_earth/models/ipcc2019/co2ToAirAboveGroundBiomassStockChange.py +0 -3
  11. hestia_earth/models/ipcc2019/co2ToAirBelowGroundBiomassStockChange.py +0 -3
  12. hestia_earth/models/ipcc2019/co2ToAirCarbonStockChange_utils.py +57 -43
  13. hestia_earth/models/ipcc2019/co2ToAirLimeHydrolysis.py +7 -5
  14. hestia_earth/models/ipcc2019/co2ToAirOrganicSoilCultivation.py +215 -0
  15. hestia_earth/models/ipcc2019/co2ToAirSoilOrganicCarbonStockChange.py +0 -3
  16. hestia_earth/models/ipcc2019/n2OToAirOrganicSoilCultivationDirect.py +161 -0
  17. hestia_earth/models/ipcc2019/organicSoilCultivation_utils.py +159 -0
  18. hestia_earth/models/mocking/search-results.json +714 -706
  19. hestia_earth/models/site/grouped_measurement.py +132 -0
  20. hestia_earth/models/utils/__init__.py +4 -3
  21. hestia_earth/models/utils/blank_node.py +38 -9
  22. hestia_earth/models/utils/constant.py +26 -20
  23. hestia_earth/models/utils/product.py +39 -1
  24. hestia_earth/models/utils/property.py +14 -6
  25. hestia_earth/models/version.py +1 -1
  26. {hestia_earth_models-0.70.1.dist-info → hestia_earth_models-0.70.2.dist-info}/METADATA +1 -1
  27. {hestia_earth_models-0.70.1.dist-info → hestia_earth_models-0.70.2.dist-info}/RECORD +42 -31
  28. tests/models/hestia/test_feedConversionRatio.py +2 -3
  29. tests/models/hestia/test_pToSurfaceWaterAquacultureSystems.py +56 -0
  30. tests/models/hestia/test_soilMeasurement.py +11 -19
  31. tests/models/ipcc2019/test_ch4ToAirEntericFermentation.py +2 -5
  32. tests/models/ipcc2019/test_ch4ToAirOrganicSoilCultivation.py +61 -0
  33. tests/models/ipcc2019/test_co2ToAirAboveGroundBiomassStockChange.py +11 -9
  34. tests/models/ipcc2019/test_co2ToAirBelowGroundBiomassStockChange.py +10 -8
  35. tests/models/ipcc2019/test_co2ToAirLimeHydrolysis.py +1 -1
  36. tests/models/ipcc2019/test_co2ToAirOrganicSoilCultivation.py +62 -0
  37. tests/models/ipcc2019/test_co2ToAirSoilOrganicCarbonStockChange.py +11 -9
  38. tests/models/ipcc2019/test_n2OToAirOrganicSoilCultivationDirect.py +61 -0
  39. tests/models/site/test_grouped_measurement.py +20 -0
  40. {hestia_earth_models-0.70.1.dist-info → hestia_earth_models-0.70.2.dist-info}/LICENSE +0 -0
  41. {hestia_earth_models-0.70.1.dist-info → hestia_earth_models-0.70.2.dist-info}/WHEEL +0 -0
  42. {hestia_earth_models-0.70.1.dist-info → hestia_earth_models-0.70.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,132 @@
1
+ from functools import reduce
2
+ from hestia_earth.schema import MeasurementMethodClassification
3
+ from hestia_earth.utils.tools import non_empty_list, flatten, is_number, list_sum
4
+
5
+ from hestia_earth.models.log import logRequirements, logShouldRun
6
+ from hestia_earth.models.utils import _include
7
+ from hestia_earth.models.utils.measurement import _new_measurement
8
+ from hestia_earth.models.utils.blank_node import get_lookup_value
9
+ from . import MODEL
10
+
11
+ REQUIREMENTS = {
12
+ "Site": {
13
+ "measurements": [
14
+ {"@type": "Measurement", "depthUpper": "", "depthLower": "", "value": ""}
15
+ ]
16
+ }
17
+ }
18
+ RETURNS = {
19
+ "Measurement": [{
20
+ "value": "0",
21
+ "depthUpper": "",
22
+ "depthLower": "",
23
+ "dates": "",
24
+ "methodClassification": "modelled using other measurements"
25
+ }]
26
+ }
27
+ LOOKUPS = {
28
+ "measurement": ["sumIs100Group", "sumMax100Group"]
29
+ }
30
+ MODEL_KEY = 'grouped_measurement'
31
+
32
+
33
+ def _measurement(term_id: str, measurement: dict):
34
+ data = _new_measurement(term_id) | _include(measurement, [
35
+ 'depthUpper', 'depthLower', 'startDate', 'endDate', 'dates'
36
+ ])
37
+ data["value"] = [0]
38
+ data["methodClassification"] = MeasurementMethodClassification.MODELLED_USING_OTHER_MEASUREMENTS.value
39
+ return data
40
+
41
+
42
+ def _measurement_term_ids(measurements: list):
43
+ return list(set([m.get('term', {}).get('@id') for m in measurements]))
44
+
45
+
46
+ def _formatDepth(depth: str):
47
+ # handle float values
48
+ return str(int(depth)) if is_number(depth) else ''
49
+
50
+
51
+ def _group_by_depth(measurements: list):
52
+ def group_by(group: dict, measurement: dict):
53
+ keys = non_empty_list([
54
+ _formatDepth(measurement.get('depthUpper')),
55
+ _formatDepth(measurement.get('depthLower'))
56
+ ])
57
+ key = '-'.join(keys) if len(keys) > 0 else 'default'
58
+ group[key] = group.get(key, []) + [measurement]
59
+ return group
60
+
61
+ return reduce(group_by, measurements, {})
62
+
63
+
64
+ def _run_measurements(site: dict, all_term_ids: list, measurements: list):
65
+ term_ids = _measurement_term_ids(measurements)
66
+
67
+ # check the total value is 100%
68
+ total_value = list_sum([
69
+ list_sum(m.get('value')) for m in measurements
70
+ ])
71
+ is_total_100 = 99.5 <= total_value <= 100.5
72
+
73
+ should_run = all([is_total_100])
74
+
75
+ other_term_ids = [v for v in all_term_ids if v not in term_ids]
76
+ for term_id in other_term_ids:
77
+ logRequirements(site, model=MODEL, term=term_id, model_key=MODEL_KEY,
78
+ total_value=total_value)
79
+
80
+ logShouldRun(site, MODEL, term_id, should_run, model_key=MODEL_KEY)
81
+
82
+ return [
83
+ _measurement(term_id=term_id, measurement=measurements[0])
84
+ for term_id in other_term_ids
85
+ ] if should_run else []
86
+
87
+
88
+ def _run(site: dict, measurements: list):
89
+ term_ids = _measurement_term_ids(measurements)
90
+ grouped_measurements = _group_by_depth(measurements)
91
+
92
+ return flatten([
93
+ _run_measurements(site, term_ids, values)
94
+ for values in grouped_measurements.values()
95
+ ]) if len(grouped_measurements.keys()) > 1 else []
96
+
97
+
98
+ def _group_by_100group(measurements: list):
99
+ def group_by(group: dict, measurement: dict):
100
+ term = measurement.get('term', {})
101
+ sum_group_key = (
102
+ get_lookup_value(term, 'sumIs100Group', skip_debug=True, model=MODEL) or
103
+ get_lookup_value(term, 'sumMax100Group', skip_debug=True, model=MODEL)
104
+ )
105
+ keys = non_empty_list([
106
+ sum_group_key,
107
+ measurement.get('startDate'),
108
+ measurement.get('endDate'),
109
+ '-'.join(measurement.get('dates') or []),
110
+ ])
111
+ key = '-'.join(keys) if len(keys) > 0 else 'default'
112
+
113
+ if all([
114
+ sum_group_key,
115
+ measurement.get('value', []),
116
+ measurement.get('depthUpper') is not None,
117
+ measurement.get('depthLower') is not None
118
+ ]):
119
+ group[key] = group.get(key, []) + [measurement]
120
+
121
+ return group
122
+
123
+ return reduce(group_by, measurements, {})
124
+
125
+
126
+ def run(site: dict):
127
+ grouped_measurements = _group_by_100group(site.get('measurements', []))
128
+ return flatten([
129
+ _run(site, values)
130
+ for values in grouped_measurements.values()
131
+ if len(values) > 1
132
+ ])
@@ -8,7 +8,7 @@ import datetime
8
8
  from functools import reduce
9
9
  import operator
10
10
  from pydash.objects import get
11
- from typing import Any, Callable
11
+ from typing import Union, List, Callable
12
12
  from hestia_earth.schema import SchemaType
13
13
  from hestia_earth.utils.api import download_hestia
14
14
  from hestia_earth.utils.tools import flatten, non_empty_list
@@ -54,8 +54,9 @@ def _load_calculated_node(node, type: SchemaType, data_state='recalculated'):
54
54
  def _unit_str(unit) -> str: return unit if isinstance(unit, str) else unit.value
55
55
 
56
56
 
57
- def _filter_list_term_unit(values: list, unit: Any):
58
- units = list(map(_unit_str, unit)) if isinstance(unit, list) else [_unit_str(unit)]
57
+ def _filter_list_term_unit(values: list, unit: Union[str, List[str]]):
58
+ units = unit if isinstance(unit, list) else[unit]
59
+ units = list(map(_unit_str, units))
59
60
  return list(filter(lambda i: i.get('term', {}).get('units') in units, values))
60
61
 
61
62
 
@@ -22,7 +22,6 @@ from hestia_earth.utils.tools import (
22
22
  list_sum,
23
23
  list_average,
24
24
  safe_parse_date,
25
- safe_parse_float,
26
25
  non_empty_list
27
26
  )
28
27
  from hestia_earth.utils.lookup_utils import (
@@ -238,16 +237,21 @@ def get_total_value(nodes: list):
238
237
 
239
238
  def _value_as(term_id: str, convert_to_property=True):
240
239
  def get_value(node: dict):
241
- property = get_node_property(node, term_id)
240
+ factor = get_node_property_value(
241
+ None, node, term_id, default=0, handle_percents=False
242
+ ) or get_lookup_value(
243
+ lookup_term=node.get('term', {}),
244
+ column=term_id
245
+ ) or 0
242
246
  # ignore node value if property is not found
243
- factor = safe_parse_float(property.get('value', 0))
244
247
  value = list_sum(node.get('value', []))
248
+ property = get_node_property(node, term_id, find_default_property=False, download_from_hestia=True)
245
249
  ratio = factor / 100 if property.get('term', {}).get('units', '') == '%' else factor
246
250
  return 0 if ratio == 0 else (value * ratio if convert_to_property else value / ratio)
247
251
  return get_value
248
252
 
249
253
 
250
- def get_total_value_converted(nodes: list, conversion_property, convert_to_property=True):
254
+ def get_total_value_converted(nodes: list, conversion_property: Union[List[str], str], convert_to_property=True):
251
255
  """
252
256
  Get the total `value` of a list of Blank Nodes converted using a property of each Blank Node.
253
257
 
@@ -371,14 +375,39 @@ def get_KG_total(nodes: list) -> list:
371
375
  return get_total_value(kg_nodes) + get_total_value_converted(kg_N_nodes, 'nitrogenContent', False)
372
376
 
373
377
 
378
+ def get_P_total(nodes: list) -> list:
379
+ """
380
+ Get the total phosphorous content of a list of Blank Node.
381
+
382
+ The result contains the values of the following nodes:
383
+ 1. Every blank node specified in `kg P` will be used.
384
+ 1. Every blank node specified in `kg N` will be multiplied by the `phosphateContentAsP` property.
385
+ 2. Every blank node specified in `kg` will be multiplied by the `phosphateContentAsP` property.
386
+
387
+ Parameters
388
+ ----------
389
+ nodes : list
390
+ A list of Blank Node.
391
+
392
+ Returns
393
+ -------
394
+ list
395
+ The phosphorous values as a list of numbers.
396
+ """
397
+ kg_P_nodes = _filter_list_term_unit(nodes, Units.KG_P)
398
+ kg_N_nodes = _filter_list_term_unit(nodes, Units.KG_N)
399
+ kg_nodes = _filter_list_term_unit(nodes, Units.KG)
400
+ return get_total_value(kg_P_nodes) + get_total_value_converted(kg_N_nodes + kg_nodes, 'phosphateContentAsP')
401
+
402
+
374
403
  def get_P2O5_total(nodes: list) -> list:
375
404
  """
376
405
  Get the total phosphate content of a list of Blank Node.
377
406
 
378
407
  The result contains the values of the following nodes:
379
- 1. Every organic fertiliser specified in `kg P2O5` will be used.
380
- 1. Every organic fertiliser specified in `kg N` will be multiplied by the `phosphateContentAsP2O5` property.
381
- 2. Every organic fertiliser specified in `kg` will be multiplied by the `phosphateContentAsP2O5` property.
408
+ 1. Every blank node specified in `kg P2O5` will be used.
409
+ 1. Every blank node specified in `kg N` will be multiplied by the `phosphateContentAsP2O5` property.
410
+ 2. Every blank node specified in `kg` will be multiplied by the `phosphateContentAsP2O5` property.
382
411
 
383
412
  Parameters
384
413
  ----------
@@ -390,10 +419,10 @@ def get_P2O5_total(nodes: list) -> list:
390
419
  list
391
420
  The phosphate values as a list of numbers.
392
421
  """
393
- kg_P_nodes = _filter_list_term_unit(nodes, Units.KG_P2O5)
422
+ kg_P2O5_nodes = _filter_list_term_unit(nodes, Units.KG_P2O5)
394
423
  kg_N_nodes = _filter_list_term_unit(nodes, Units.KG_N)
395
424
  kg_nodes = _filter_list_term_unit(nodes, Units.KG)
396
- return get_total_value(kg_P_nodes) + get_total_value_converted(kg_N_nodes + kg_nodes, 'phosphateContentAsP2O5')
425
+ return get_total_value(kg_P2O5_nodes) + get_total_value_converted(kg_N_nodes + kg_nodes, 'phosphateContentAsP2O5')
397
426
 
398
427
 
399
428
  def convert_to_nitrogen(node: dict, model: str, blank_nodes: list, **log_args):
@@ -19,7 +19,7 @@ class Units(Enum):
19
19
  KG_CO2 = 'kg CO2'
20
20
  KG_K = 'kg K'
21
21
  KG_K2O = 'kg K2O'
22
- KG_MGCO3 = 'kg MgCO3'
22
+ KG_CAMGCO32 = 'kg CaMg(CO3)2'
23
23
  KG_N = 'kg N'
24
24
  KG_N2 = 'kg N2'
25
25
  KG_N2O = 'kg N2O'
@@ -48,58 +48,62 @@ C = 12.012
48
48
  CA = 40.078
49
49
  H = 1.008
50
50
  K = 39.098
51
+ MG = 24.305
51
52
  N = 14.007
52
53
  _O = 15.999
53
54
  P = 30.974
54
55
  ATOMIC_WEIGHT_CONVERSIONS = {
55
56
  Units.KG_P.value: {
56
- Units.KG_P2O5.value: (P*2) / (P*2 + _O*5), # Conv_Mol_P_P2O5
57
- Units.KG_PO43.value: P / ((P + _O*4)*3) # Conv_Mol_P_PO43-
57
+ Units.KG_P2O5.value: (P*2) / (P*2 + _O*5),
58
+ Units.KG_PO43.value: P / ((P + _O*4)*3)
58
59
  },
59
60
  Units.KG_PO43.value: {
60
- Units.KG_P2O5.value: ((P + _O*4)*3) / (P*2 + _O*5) # Conv_Mol_PO43-_P2O5
61
+ Units.KG_P2O5.value: ((P + _O*4)*3) / (P*2 + _O*5)
62
+ },
63
+ Units.KG_P2O5.value: {
64
+ Units.KG_P.value: (P*2 + _O*5) / (P*2)
61
65
  },
62
66
  Units.KG_K.value: {
63
- Units.KG_K2O.value: (K*2) / (K*2 + _O) # Conv_Mol_K_K2O
67
+ Units.KG_K2O.value: (K*2) / (K*2 + _O)
64
68
  },
65
69
  Units.KG_CA.value: {
66
- Units.KG_CAO.value: CA / (CA + _O) # Conv_Mol_Ca_CaO
70
+ Units.KG_CAO.value: CA / (CA + _O)
67
71
  },
68
72
  Units.KG_CAO.value: {
69
- Units.KG_CACO3.value: (CA + _O) / (CA + C + _O*3) # Conv_Mol_CaO_CaCO3
73
+ Units.KG_CACO3.value: (CA + _O) / (CA + C + _O*3)
70
74
  },
71
75
  Units.KG_CACO3.value: {
72
- Units.KG_CO2.value: 0.12
76
+ Units.KG_CO2.value: (CA + C + _O*3) / C
73
77
  },
74
- Units.KG_MGCO3.value: {
75
- Units.KG_CO2.value: 0.13
78
+ Units.KG_CAMGCO32.value: {
79
+ Units.KG_CO2.value: (CA + MG + (C + _O*3)*2) / (C*2)
76
80
  },
77
81
  Units.KG_CH4.value: {
78
- Units.TO_C.value: (C + H*4) / C # Conv_Mol_CH4C_CH4
82
+ Units.TO_C.value: (C + H*4) / C
79
83
  },
80
84
  Units.KG_CO2.value: {
81
- Units.TO_C.value: (C + _O*2) / C # Conv_Mol_CO2C_CO2
85
+ Units.TO_C.value: (C + _O*2) / C
82
86
  },
83
87
  Units.KG_NOX.value: {
84
- Units.TO_N.value: (N + _O) / N # Conv_Mol_NON_NO
88
+ Units.TO_N.value: (N + _O) / N
85
89
  },
86
90
  Units.KG_N2.value: {
87
91
  Units.TO_N.value: 1
88
92
  },
89
93
  Units.KG_N2O.value: {
90
- Units.TO_N.value: (N*2 + _O) / (N*2) # Conv_Mol_N2ON_N2O
94
+ Units.TO_N.value: (N*2 + _O) / (N*2)
91
95
  },
92
96
  Units.KG_NO2.value: {
93
- Units.TO_N.value: (N + _O*2) / N # Conv_Mol_NO2N_NO2
97
+ Units.TO_N.value: (N + _O*2) / N
94
98
  },
95
99
  Units.KG_NO3.value: {
96
- Units.TO_N.value: (N + _O*3) / N # Conv_Mol_NO3N_NO3
100
+ Units.TO_N.value: (N + _O*3) / N
97
101
  },
98
102
  Units.KG_NH3.value: {
99
- Units.TO_N.value: (N + H*3) / N # Conv_Mol_NH3N_NH3
103
+ Units.TO_N.value: (N + H*3) / N
100
104
  },
101
105
  Units.KG_NH4.value: {
102
- Units.TO_N.value: (N + H*4) / N # Conv_Mol_NH4N_NH4
106
+ Units.TO_N.value: (N + H*4) / N
103
107
  },
104
108
  Units.KW_H.value: {
105
109
  Units.MJ.value: 3.6
@@ -113,8 +117,10 @@ def get_atomic_conversion(src_unit: Units, dest_unit: Units, default_value=1):
113
117
  return ATOMIC_WEIGHT_CONVERSIONS.get(src_key, {}).get(dest_key, default_value)
114
118
 
115
119
 
116
- def convert_to_unit(node: dict, dest_unit: Units):
117
- return list_sum(node.get('value', [])) * get_atomic_conversion(node.get('term', {}).get('units'), dest_unit)
120
+ def convert_to_unit(node: dict, dest_unit: Units, default_value=None):
121
+ value = list_sum(node.get('value', []), default=None)
122
+ conversion = get_atomic_conversion(node.get('term', {}).get('units'), dest_unit, default_value=None)
123
+ return value / conversion if all([value is not None, conversion is not None]) else default_value
118
124
 
119
125
 
120
126
  def convert_to_N(node: dict):
@@ -166,6 +166,12 @@ PRODUCT_UNITS_CONVERSIONS = {
166
166
  ],
167
167
  Units.KG_VS.value: [
168
168
  ('volatileSolidsContent', True)
169
+ ],
170
+ Units.KG_P.value: [
171
+ ('phosphorusContentAsP', True)
172
+ ],
173
+ Units.KG_P2O5.value: [
174
+ ('phosphateContentAsP2O5', True)
169
175
  ]
170
176
  },
171
177
  Units.KG_N.value: {
@@ -175,6 +181,14 @@ PRODUCT_UNITS_CONVERSIONS = {
175
181
  Units.KG_VS.value: [
176
182
  ('nitrogenContent', False),
177
183
  ('volatileSolidsContent', True)
184
+ ],
185
+ Units.KG_P.value: [
186
+ ('nitrogenContent', False),
187
+ ('phosphorusContentAsP', True)
188
+ ],
189
+ Units.KG_P2O5.value: [
190
+ ('nitrogenContent', False),
191
+ ('phosphateContentAsP2O5', True)
178
192
  ]
179
193
  },
180
194
  Units.KG_VS.value: {
@@ -215,6 +229,12 @@ PRODUCT_UNITS_CONVERSIONS = {
215
229
  ],
216
230
  True
217
231
  )
232
+ ],
233
+ Units.KG_P.value: [
234
+ ('phosphorusContentAsP', True)
235
+ ],
236
+ Units.KG_P2O5.value: [
237
+ ('phosphateContentAsP2O5', True)
218
238
  ]
219
239
  },
220
240
  Units.KG_COLD_CARCASS_WEIGHT.value: {
@@ -225,6 +245,12 @@ PRODUCT_UNITS_CONVERSIONS = {
225
245
  Units.KG_COLD_CARCASS_WEIGHT.value: [],
226
246
  Units.KG_READY_TO_COOK_WEIGHT.value: [
227
247
  ('processingConversionColdCarcassWeightToReadyToCookWeight', True)
248
+ ],
249
+ Units.KG_P.value: [
250
+ ('phosphorusContentAsP', True)
251
+ ],
252
+ Units.KG_P2O5.value: [
253
+ ('phosphateContentAsP2O5', True)
228
254
  ]
229
255
  },
230
256
  Units.KG_COLD_DRESSED_CARCASS_WEIGHT.value: {
@@ -237,6 +263,12 @@ PRODUCT_UNITS_CONVERSIONS = {
237
263
  Units.KG_COLD_CARCASS_WEIGHT.value: [],
238
264
  Units.KG_READY_TO_COOK_WEIGHT.value: [
239
265
  ('processingConversionColdDressedCarcassWeightToReadyToCookWeight', True)
266
+ ],
267
+ Units.KG_P.value: [
268
+ ('phosphorusContentAsP', True)
269
+ ],
270
+ Units.KG_P2O5.value: [
271
+ ('phosphateContentAsP2O5', True)
240
272
  ]
241
273
  },
242
274
  Units.KG_READY_TO_COOK_WEIGHT.value: {
@@ -262,7 +294,13 @@ PRODUCT_UNITS_CONVERSIONS = {
262
294
  Units.KG_COLD_DRESSED_CARCASS_WEIGHT.value: [
263
295
  ('processingConversionColdDressedCarcassWeightToReadyToCookWeight', False)
264
296
  ],
265
- Units.KG_READY_TO_COOK_WEIGHT.value: []
297
+ Units.KG_READY_TO_COOK_WEIGHT.value: [],
298
+ Units.KG_P.value: [
299
+ ('phosphorusContentAsP', True)
300
+ ],
301
+ Units.KG_P2O5.value: [
302
+ ('phosphateContentAsP2O5', True)
303
+ ]
266
304
  },
267
305
  Units.HEAD.value: {
268
306
  Units.KG_LIVEWEIGHT.value: [
@@ -50,7 +50,12 @@ def find_term_property(term, property: str, default=None) -> dict:
50
50
  return find_term_match(props, property, default)
51
51
 
52
52
 
53
- def get_node_property(node: dict, property: str, find_default_property: bool = True) -> dict:
53
+ def get_node_property(
54
+ node: dict,
55
+ property: str,
56
+ find_default_property: bool = True,
57
+ download_from_hestia: bool = False
58
+ ) -> dict:
54
59
  """
55
60
  Get the property by `@id` linked to the Blank Node in the glossary.
56
61
 
@@ -66,6 +71,8 @@ def get_node_property(node: dict, property: str, find_default_property: bool = T
66
71
  The `term.@id` of the property. Example: `nitrogenContent`.
67
72
  find_default_property : bool
68
73
  Default to fetching the property from the `defaultProperties` of the `Term`.
74
+ download_from_hestia : bool
75
+ Default to downloading the Term from HESTIA.
69
76
 
70
77
  Returns
71
78
  -------
@@ -76,7 +83,9 @@ def get_node_property(node: dict, property: str, find_default_property: bool = T
76
83
  return find_term_property(node.get('term', {}), property, {}) if all([
77
84
  find_default_property,
78
85
  prop is None
79
- ]) else (prop or {})
86
+ ]) else (
87
+ prop or ({'term': download_term(property, TermTermType.PROPERTY)} if download_from_hestia else {})
88
+ )
80
89
 
81
90
 
82
91
  def node_has_no_property(term_id: str):
@@ -91,9 +100,8 @@ def node_property_lookup_value(model: str, node_term: dict, prop_id: str, defaul
91
100
  # as the lookup table might not exist, we are making sure we return `0` in thise case
92
101
  try:
93
102
  lookup_name = f"{node_term.get('termType')}-property.csv"
94
- lookup = download_lookup(lookup_name)
95
103
  term_id = node_term.get('@id')
96
- lookup_value = get_table_value(lookup, 'termid', term_id, column_name(prop_id))
104
+ lookup_value = get_table_value(download_lookup(lookup_name), 'termid', term_id, column_name(prop_id))
97
105
  value = extract_grouped_data(lookup_value, 'Avg') if (
98
106
  isinstance(lookup_value, str) and 'Avg' in lookup_value
99
107
  ) else lookup_value
@@ -104,8 +112,8 @@ def node_property_lookup_value(model: str, node_term: dict, prop_id: str, defaul
104
112
 
105
113
 
106
114
  def get_node_property_value(model: str, node: dict, prop_id: str, default=None, handle_percents=True, **log_args):
107
- prop = get_node_property(node, prop_id)
108
- term = (prop or {}).get('term') or download_term(prop_id, TermTermType.PROPERTY)
115
+ prop = get_node_property(node, prop_id, download_from_hestia=True)
116
+ term = (prop or {}).get('term')
109
117
  units = (term or {}).get('units')
110
118
  value = prop.get('value') if prop else node_property_lookup_value(model, node.get('term', {}), prop_id, **log_args)
111
119
  return default if value is None else (value / 100 if units == '%' and handle_percents else value)
@@ -1 +1 @@
1
- VERSION = '0.70.1'
1
+ VERSION = '0.70.2'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: hestia-earth-models
3
- Version: 0.70.1
3
+ Version: 0.70.2
4
4
  Summary: HESTIA's set of modules for filling gaps in the activity data using external datasets (e.g. populating soil properties with a geospatial dataset using provided coordinates) and internal lookups (e.g. populating machinery use from fuel use). Includes rules for when gaps should be filled versus not (e.g. never gap fill yield, gap fill crop residue if yield provided etc.).
5
5
  Home-page: https://gitlab.com/hestia-earth/hestia-engine-models
6
6
  Author: HESTIA Team