hestia-earth-models 0.67.0__py3-none-any.whl → 0.68.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.
Files changed (161) hide show
  1. hestia_earth/models/aware/scarcityWeightedWaterUse.py +5 -6
  2. hestia_earth/models/blonkConsultants2016/ch4ToAirNaturalVegetationBurning.py +1 -1
  3. hestia_earth/models/blonkConsultants2016/co2ToAirAboveGroundBiomassStockChangeLandUseChange.py +1 -1
  4. hestia_earth/models/blonkConsultants2016/n2OToAirNaturalVegetationBurningDirect.py +1 -1
  5. hestia_earth/models/blonkConsultants2016/utils.py +9 -9
  6. hestia_earth/models/cache_sites.py +26 -14
  7. hestia_earth/models/chaudharyBrooks2018/damageToTerrestrialEcosystemsLandOccupation.py +2 -2
  8. hestia_earth/models/chaudharyBrooks2018/damageToTerrestrialEcosystemsLandTransformation.py +2 -2
  9. hestia_earth/models/chaudharyBrooks2018/utils.py +13 -8
  10. hestia_earth/models/cml2001Baseline/abioticResourceDepletionFossilFuels.py +2 -3
  11. hestia_earth/models/cml2001Baseline/abioticResourceDepletionMineralsAndMetals.py +1 -1
  12. hestia_earth/models/cml2001Baseline/resourceUseEnergyDepletionDuringCycle.py +5 -10
  13. hestia_earth/models/config/Cycle.json +15 -0
  14. hestia_earth/models/config/ImpactAssessment.json +14 -1
  15. hestia_earth/models/config/Site.json +8 -0
  16. hestia_earth/models/cycle/completeness/freshForage.py +7 -3
  17. hestia_earth/models/cycle/excretaKgMass.py +2 -2
  18. hestia_earth/models/cycle/inorganicFertiliser.py +67 -17
  19. hestia_earth/models/cycle/materialAndSubstrate.py +3 -2
  20. hestia_earth/models/cycle/pastureGrass.py +3 -3
  21. hestia_earth/models/dammgen2009/noxToAirExcreta.py +1 -1
  22. hestia_earth/models/ecoinventV3AndEmberClimate/__init__.py +1 -1
  23. hestia_earth/models/ecoinventV3AndEmberClimate/utils.py +2 -6
  24. hestia_earth/models/emissionNotRelevant/__init__.py +4 -4
  25. hestia_earth/models/environmentalFootprintV3_1/environmentalFootprintSingleOverallScore.py +60 -46
  26. hestia_earth/models/environmentalFootprintV3_1/photochemicalOzoneCreationPotentialHumanHealthNmvocEq.py +36 -0
  27. hestia_earth/models/environmentalFootprintV3_1/scarcityWeightedWaterUse.py +2 -2
  28. hestia_earth/models/environmentalFootprintV3_1/soilQualityIndexLandOccupation.py +9 -8
  29. hestia_earth/models/environmentalFootprintV3_1/soilQualityIndexLandTransformation.py +45 -34
  30. hestia_earth/models/environmentalFootprintV3_1/soilQualityIndexTotalLandUseEffects.py +24 -21
  31. hestia_earth/models/faostat2018/coldCarcassWeightPerHead.py +2 -2
  32. hestia_earth/models/faostat2018/coldDressedCarcassWeightPerHead.py +2 -2
  33. hestia_earth/models/faostat2018/liveweightPerHead.py +7 -8
  34. hestia_earth/models/faostat2018/product/price.py +34 -28
  35. hestia_earth/models/faostat2018/readyToCookWeightPerHead.py +2 -2
  36. hestia_earth/models/faostat2018/utils.py +15 -27
  37. hestia_earth/models/frischknechtEtAl2000/ionisingRadiationKbqU235Eq.py +16 -9
  38. hestia_earth/models/geospatialDatabase/altitude.py +60 -0
  39. hestia_earth/models/geospatialDatabase/croppingIntensity.py +1 -1
  40. hestia_earth/models/geospatialDatabase/ecoClimateZone.py +2 -2
  41. hestia_earth/models/geospatialDatabase/longFallowRatio.py +1 -1
  42. hestia_earth/models/geospatialDatabase/utils.py +4 -1
  43. hestia_earth/models/globalCropWaterModel2008/rootingDepth.py +2 -3
  44. hestia_earth/models/haversineFormula/transport/distance.py +3 -3
  45. hestia_earth/models/hestia/landCover.py +72 -45
  46. hestia_earth/models/hestia/landTransformation100YearAverageDuringCycle.py +1 -1
  47. hestia_earth/models/hestia/landTransformation20YearAverageDuringCycle.py +1 -1
  48. hestia_earth/models/hestia/seed_emissions.py +11 -7
  49. hestia_earth/models/impact_assessment/__init__.py +3 -3
  50. hestia_earth/models/ipcc2019/aboveGroundBiomass.py +1 -1
  51. hestia_earth/models/ipcc2019/animal/fatContent.py +1 -1
  52. hestia_earth/models/ipcc2019/animal/hoursWorkedPerDay.py +1 -1
  53. hestia_earth/models/ipcc2019/animal/liveweightGain.py +1 -1
  54. hestia_earth/models/ipcc2019/animal/liveweightPerHead.py +1 -1
  55. hestia_earth/models/ipcc2019/animal/milkYieldPerAnimal.py +1 -1
  56. hestia_earth/models/ipcc2019/animal/pastureGrass.py +1 -1
  57. hestia_earth/models/ipcc2019/animal/pregnancyRateTotal.py +1 -1
  58. hestia_earth/models/ipcc2019/animal/trueProteinContent.py +1 -1
  59. hestia_earth/models/ipcc2019/animal/utils.py +5 -7
  60. hestia_earth/models/ipcc2019/animal/weightAtMaturity.py +1 -1
  61. hestia_earth/models/ipcc2019/belowGroundBiomass.py +1 -1
  62. hestia_earth/models/ipcc2019/ch4ToAirEntericFermentation.py +2 -2
  63. hestia_earth/models/ipcc2019/ch4ToAirExcreta.py +6 -7
  64. hestia_earth/models/ipcc2019/ch4ToAirFloodedRice.py +5 -3
  65. hestia_earth/models/ipcc2019/co2ToAirCarbonStockChange_utils.py +1 -1
  66. hestia_earth/models/ipcc2019/croppingDuration.py +3 -6
  67. hestia_earth/models/ipcc2019/nonCo2EmissionsToAirNaturalVegetationBurning.py +947 -0
  68. hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_1_utils.py +4 -4
  69. hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_2_utils.py +1 -1
  70. hestia_earth/models/ipcc2019/pastureGrass.py +1 -1
  71. hestia_earth/models/koble2014/residueBurnt.py +5 -7
  72. hestia_earth/models/koble2014/residueRemoved.py +5 -7
  73. hestia_earth/models/lcImpactAllEffects100Years/damageToHumanHealthWaterStress.py +2 -2
  74. hestia_earth/models/lcImpactAllEffectsInfinite/damageToHumanHealthWaterStress.py +2 -2
  75. hestia_earth/models/lcImpactCertainEffects100Years/damageToHumanHealthWaterStress.py +2 -2
  76. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToHumanHealthWaterStress.py +2 -2
  77. hestia_earth/models/log.py +1 -1
  78. hestia_earth/models/mocking/search-results.json +3477 -1045
  79. hestia_earth/models/site/management.py +1 -1
  80. hestia_earth/models/site/post_checks/__init__.py +3 -2
  81. hestia_earth/models/site/post_checks/country.py +9 -0
  82. hestia_earth/models/site/pre_checks/__init__.py +3 -2
  83. hestia_earth/models/site/pre_checks/country.py +9 -0
  84. hestia_earth/models/utils/__init__.py +1 -16
  85. hestia_earth/models/utils/blank_node.py +89 -36
  86. hestia_earth/models/utils/completeness.py +3 -2
  87. hestia_earth/models/utils/cycle.py +5 -4
  88. hestia_earth/models/utils/ecoClimateZone.py +2 -2
  89. hestia_earth/models/utils/emission.py +5 -5
  90. hestia_earth/models/utils/feedipedia.py +6 -6
  91. hestia_earth/models/utils/impact_assessment.py +6 -6
  92. hestia_earth/models/utils/indicator.py +9 -7
  93. hestia_earth/models/utils/inorganicFertiliser.py +4 -6
  94. hestia_earth/models/utils/input.py +6 -5
  95. hestia_earth/models/utils/lookup.py +35 -105
  96. hestia_earth/models/utils/management.py +4 -4
  97. hestia_earth/models/utils/measurement.py +6 -7
  98. hestia_earth/models/utils/method.py +20 -0
  99. hestia_earth/models/utils/practice.py +4 -5
  100. hestia_earth/models/utils/product.py +4 -5
  101. hestia_earth/models/utils/property.py +12 -22
  102. hestia_earth/models/utils/site.py +14 -8
  103. hestia_earth/models/utils/term.py +27 -1
  104. hestia_earth/models/version.py +1 -1
  105. hestia_earth/orchestrator/log.py +0 -11
  106. hestia_earth/orchestrator/models/__init__.py +17 -4
  107. hestia_earth/orchestrator/strategies/run/add_blank_node_if_missing.py +2 -20
  108. {hestia_earth_models-0.67.0.dist-info → hestia_earth_models-0.68.0.dist-info}/METADATA +2 -2
  109. {hestia_earth_models-0.67.0.dist-info → hestia_earth_models-0.68.0.dist-info}/RECORD +159 -151
  110. tests/models/cml2001Baseline/test_abioticResourceDepletionFossilFuels.py +3 -3
  111. tests/models/cml2001Baseline/test_resourceUseEnergyDepletionDuringCycle.py +68 -35
  112. tests/models/cycle/test_coldCarcassWeightPerHead.py +1 -1
  113. tests/models/cycle/test_coldDressedCarcassWeightPerHead.py +1 -1
  114. tests/models/cycle/test_concentrateFeed.py +1 -1
  115. tests/models/cycle/test_energyContentLowerHeatingValue.py +1 -1
  116. tests/models/cycle/test_excretaKgMass.py +1 -1
  117. tests/models/cycle/test_feedConversionRatio.py +3 -3
  118. tests/models/cycle/test_pastureGrass.py +1 -1
  119. tests/models/cycle/test_readyToCookWeightPerHead.py +1 -1
  120. tests/models/environmentalFootprintV3_1/test_environmentalFootprintSingleOverallScore.py +38 -8
  121. tests/models/environmentalFootprintV3_1/test_photochemicalOzoneCreationPotentialHumanHealthNmvocEq.py +30 -0
  122. tests/models/environmentalFootprintV3_1/test_soilQualityIndexLandTransformation.py +65 -36
  123. tests/models/environmentalFootprintV3_1/test_soilQualityIndexTotalLandUseEffects.py +30 -7
  124. tests/models/faostat2018/product/test_price.py +27 -14
  125. tests/models/faostat2018/test_faostat_utils.py +4 -24
  126. tests/models/faostat2018/test_liveweightPerHead.py +9 -9
  127. tests/models/globalCropWaterModel2008/test_rootingDepth.py +7 -3
  128. tests/models/haversineFormula/transport/test_distance.py +1 -1
  129. tests/models/hestia/test_landCover.py +53 -5
  130. tests/models/ipcc2019/animal/test_pastureGrass.py +5 -3
  131. tests/models/ipcc2019/test_aboveGroundCropResidueTotal.py +4 -4
  132. tests/models/ipcc2019/test_belowGroundCropResidue.py +4 -4
  133. tests/models/ipcc2019/test_ch4ToAirEntericFermentation.py +10 -10
  134. tests/models/ipcc2019/test_croppingDuration.py +1 -1
  135. tests/models/ipcc2019/test_nonCo2EmissionsToAirNaturalVegetationBurning.py +83 -0
  136. tests/models/ipcc2019/test_organicCarbonPerHa.py +12 -12
  137. tests/models/ipcc2019/test_pastureGrass.py +5 -3
  138. tests/models/pooreNemecek2018/test_excretaKgN.py +5 -5
  139. tests/models/pooreNemecek2018/test_excretaKgVs.py +2 -2
  140. tests/models/site/post_checks/test_country.py +6 -0
  141. tests/models/site/pre_checks/test_cache_geospatialDatabase.py +1 -1
  142. tests/models/site/pre_checks/test_country.py +12 -0
  143. tests/models/site/test_management.py +1 -4
  144. tests/models/test_ecoinventV3.py +7 -3
  145. tests/models/utils/test_blank_node.py +17 -177
  146. tests/models/utils/test_dataCompleteness.py +5 -5
  147. tests/models/utils/test_emission.py +2 -2
  148. tests/models/utils/test_indicator.py +2 -2
  149. tests/models/utils/test_input.py +2 -2
  150. tests/models/utils/test_measurement.py +2 -4
  151. tests/models/utils/test_practice.py +4 -2
  152. tests/models/utils/test_product.py +2 -2
  153. tests/models/utils/test_property.py +4 -2
  154. tests/models/utils/test_site.py +7 -0
  155. tests/orchestrator/models/test_transformations.py +4 -1
  156. tests/orchestrator/strategies/run/test_add_blank_node_if_missing.py +4 -9
  157. hestia_earth/models/environmentalFootprintV3_1/utils.py +0 -17
  158. tests/models/utils/test_lookup.py +0 -10
  159. {hestia_earth_models-0.67.0.dist-info → hestia_earth_models-0.68.0.dist-info}/LICENSE +0 -0
  160. {hestia_earth_models-0.67.0.dist-info → hestia_earth_models-0.68.0.dist-info}/WHEEL +0 -0
  161. {hestia_earth_models-0.67.0.dist-info → hestia_earth_models-0.68.0.dist-info}/top_level.txt +0 -0
@@ -1,10 +1,13 @@
1
1
  from typing import Optional, List
2
- from numpy import recarray
3
- from hestia_earth.schema import SchemaType
4
2
  from hestia_earth.utils.lookup import (
5
- download_lookup, get_table_value, column_name, extract_grouped_data, _get_single_table_value
3
+ download_lookup,
4
+ get_table_value,
5
+ column_name,
6
+ extract_grouped_data,
7
+ _get_single_table_value,
8
+ lookup_term_ids
6
9
  )
7
- from hestia_earth.utils.tools import list_sum, safe_parse_float, non_empty_list
10
+ from hestia_earth.utils.tools import list_sum, safe_parse_float
8
11
 
9
12
  from ..log import debugValues, log_as_table, debugMissingLookup
10
13
 
@@ -15,13 +18,11 @@ def _node_value(node):
15
18
 
16
19
 
17
20
  def _factor_value(model: str, term_id: str, lookup_name: str, lookup_col: str, grouped_key: Optional[str] = None):
18
- lookup = download_lookup(lookup_name)
19
-
20
21
  def get_value(data: dict):
21
22
  node_term_id = data.get('term', {}).get('@id')
22
23
  grouped_data_key = grouped_key or data.get('methodModel', {}).get('@id')
23
24
  value = _node_value(data)
24
- coefficient = get_table_value(lookup, 'termid', node_term_id, column_name(lookup_col))
25
+ coefficient = get_region_lookup_value(lookup_name, node_term_id, lookup_col, model=model, term=term_id)
25
26
  # value is either a number or matching between a model and a value (restrict value to specific model only)
26
27
  coefficient = safe_parse_float(
27
28
  extract_grouped_data(coefficient, grouped_data_key), None
@@ -50,8 +51,8 @@ def all_factor_value(
50
51
  values = list(map(_factor_value(model, term_id, lookup_name, lookup_col, grouped_key), blank_nodes))
51
52
 
52
53
  has_values = len(values) > 0
53
- missing_values = set([v.get('id') for v in values if v.get('value') is not None and v.get('coefficient') is None])
54
- all_with_factors = all([v.get('coefficient') is not None for v in values if v.get('value') is not None])
54
+ missing_values = set([v.get('id') for v in values if v.get('value') and v.get('coefficient') is None])
55
+ all_with_factors = not missing_values
55
56
 
56
57
  for missing_value in missing_values:
57
58
  debugMissingLookup(lookup_name, 'termid', missing_value, lookup_col, None, model=model, term=term_id)
@@ -65,18 +66,14 @@ def all_factor_value(
65
66
  values = [float((v.get('value') or 0) * (v.get('coefficient') or 0)) for v in values]
66
67
 
67
68
  # fail if some factors are missing
68
- return None if not all_with_factors else (
69
- list_sum(values) if has_values else default_no_values
70
- )
69
+ return None if not all_with_factors else (list_sum(values) if has_values else default_no_values)
71
70
 
72
71
 
73
72
  def _term_factor_value(model: str, term_id: str, lookup_name: str, lookup_term_id: str, group_key: str = None):
74
- lookup = download_lookup(lookup_name, False) # avoid saving in memory as there could be many different files used
75
-
76
73
  def get_value(data: dict):
77
74
  node_term_id = data.get('term', {}).get('@id')
78
75
  value = _node_value(data)
79
- coefficient = get_table_value(lookup, 'termid', lookup_term_id, column_name(node_term_id))
76
+ coefficient = get_region_lookup_value(lookup_name, lookup_term_id, node_term_id, model=model, term=term_id)
80
77
  coefficient = safe_parse_float(extract_grouped_data(coefficient, group_key) if group_key else coefficient)
81
78
  if value is not None and coefficient is not None:
82
79
  debugValues(data, model=model, term=term_id,
@@ -109,98 +106,31 @@ def _aware_factor_value(model: str, term_id: str, lookup_name: str, aware_id: st
109
106
  return get_value
110
107
 
111
108
 
112
- _ALLOW_ALL = 'all'
113
-
114
-
115
- def _is_site(site: dict):
116
- return site.get('@type', site.get('type')) == SchemaType.SITE.value if site else None
117
-
118
-
119
- def _get_sites(node: dict):
120
- site = node.get('site', node.get('cycle', {}).get('site'))
121
- other_sites = node.get('otherSites', node.get('cycle', {}).get('otherSites', []))
122
- return non_empty_list([site] + other_sites)
123
-
124
-
125
- def _get_site_types(node: dict):
126
- sites = [node] if _is_site(node) else _get_sites(node)
127
- return non_empty_list([site.get('siteType') for site in sites])
128
-
129
-
130
- def _model_lookup_values(model: str, term: dict, restriction: str):
131
- lookup = download_lookup(f"{term.get('termType')}-model-{restriction}.csv")
132
- values = get_table_value(lookup, 'termid', term.get('@id'), column_name(model))
133
- return (values or _ALLOW_ALL).split(';') if isinstance(values, str) else _ALLOW_ALL
134
-
135
-
136
- def is_model_siteType_allowed(model: str, term: dict, data: dict):
137
- site_types = _get_site_types(data)
138
- allowed_values = _model_lookup_values(model, term, 'siteTypesAllowed')
139
- return True if _ALLOW_ALL in allowed_values or not site_types else any([
140
- (site_type in allowed_values) for site_type in site_types
141
- ])
142
-
143
-
144
- def _lookup_values(term: dict, column: str):
145
- lookup = download_lookup(f"{term.get('termType')}.csv")
146
- values = get_table_value(lookup, 'termid', term.get('@id'), column_name(column))
147
- return (values or _ALLOW_ALL).split(';') if isinstance(values, str) else _ALLOW_ALL
148
-
149
-
150
- def is_siteType_allowed(data: dict, term: dict):
151
- site_types = _get_site_types(data)
152
- allowed_values = _lookup_values(term, 'siteTypesAllowed')
153
- return True if _ALLOW_ALL in allowed_values or not site_types else any([
154
- (site_type in allowed_values) for site_type in site_types
155
- ])
156
-
157
-
158
- def is_product_termType_allowed(data: dict, term: dict):
159
- products = data.get('products', [])
160
- values = non_empty_list([p.get('term', {}).get('termType') for p in products])
161
- allowed_values = _lookup_values(term, 'productTermTypesAllowed')
162
- return True if any([
163
- _ALLOW_ALL in allowed_values,
164
- len(values) == 0
165
- ]) else any([value in allowed_values for value in values])
166
-
167
-
168
- def is_product_id_allowed(data: dict, term: dict):
169
- products = data.get('products', [])
170
- values = non_empty_list([p.get('term', {}).get('@id') for p in products])
171
- allowed_values = _lookup_values(term, 'productTermIdsAllowed')
172
- return True if any([
173
- _ALLOW_ALL in allowed_values,
174
- len(values) == 0
175
- ]) else any([value in allowed_values for value in values])
176
-
177
-
178
- def is_input_termType_allowed(data: dict, term: dict):
179
- inputs = data.get('inputs', [])
180
- values = non_empty_list([p.get('term', {}).get('termType') for p in inputs])
181
- allowed_values = _lookup_values(term, 'inputTermTypesAllowed')
182
- return True if any([
183
- _ALLOW_ALL in allowed_values,
184
- len(values) == 0
185
- ]) else any([value in allowed_values for value in values])
186
-
187
-
188
- def is_input_id_allowed(data: dict, term: dict):
189
- inputs = data.get('inputs', [])
190
- values = non_empty_list([p.get('term', {}).get('@id') for p in inputs])
191
- allowed_values = _lookup_values(term, 'inputTermIdsAllowed')
192
- return True if any([
193
- _ALLOW_ALL in allowed_values,
194
- len(values) == 0
195
- ]) else any([value in allowed_values for value in values])
109
+ def _country_in_lookup(country_id: str):
110
+ def in_lookup(lookup_name: str):
111
+ return (
112
+ download_lookup(lookup_name.replace('region', country_id)) is not None or
113
+ country_id in lookup_term_ids(download_lookup(lookup_name))
114
+ )
115
+ return in_lookup
196
116
 
197
117
 
198
- def fallback_country(country_id: str, lookup_arrays: List[recarray]) -> str:
118
+ def fallback_country(country_id: str, lookups: List[str]) -> str:
199
119
  """
200
- Given a site dict with 'country_id' location term, and lookup table,
201
- checks if a location can be used in lookup file
202
- else fallback to the default "region-world"
120
+ Given a country `@id`, and lookup tables, checks if a location can be used in lookup file
121
+ else fallback to the default "region-world".
203
122
  """
204
- is_in_lookup = lambda v: all(v in array['termid'] for array in lookup_arrays) # noqa: E731
123
+ is_in_lookup = lambda v: all(map(_country_in_lookup(v), lookups)) # noqa: E731
205
124
  fallback_id = 'region-world'
206
- return country_id if is_in_lookup(country_id) else fallback_id if is_in_lookup(fallback_id) else None
125
+ return country_id if country_id and is_in_lookup(country_id) else fallback_id if is_in_lookup(fallback_id) else None
126
+
127
+
128
+ def get_region_lookup_value(lookup_name: str, term_id: str, column: str, **log_args):
129
+ # for performance, try to load the region specific lookup if exists
130
+ lookup = (
131
+ download_lookup(lookup_name.replace('region-', f"{term_id}-"))
132
+ if lookup_name and lookup_name.startswith('region-') else None
133
+ ) or download_lookup(lookup_name)
134
+ value = get_table_value(lookup, 'termid', term_id, column_name(column))
135
+ debugMissingLookup(lookup_name, 'termid', term_id, column, value, **log_args)
136
+ return value
@@ -1,11 +1,11 @@
1
1
  from hestia_earth.schema import SchemaType
2
2
  from hestia_earth.utils.model import linked_node
3
- from hestia_earth.utils.api import download_hestia
4
3
 
5
- from . import _term_id, _include_model
4
+ from .term import download_term
5
+ from .method import include_model
6
6
 
7
7
 
8
8
  def _new_management(term, model=None):
9
9
  node = {'@type': SchemaType.MANAGEMENT.value}
10
- node['term'] = linked_node(term if isinstance(term, dict) else download_hestia(_term_id(term)))
11
- return _include_model(node, model)
10
+ node['term'] = linked_node(term if isinstance(term, dict) else download_term(term))
11
+ return include_model(node, model)
@@ -3,16 +3,15 @@ from collections.abc import Iterable
3
3
  from functools import reduce
4
4
  from statistics import mode, mean
5
5
  from typing import Any, Optional, Union
6
-
7
6
  from hestia_earth.schema import MeasurementMethodClassification, SchemaType
8
- from hestia_earth.utils.api import download_hestia
9
7
  from hestia_earth.utils.model import linked_node
10
8
  from hestia_earth.utils.tools import non_empty_list, flatten, safe_parse_float
11
9
  from hestia_earth.utils.date import diff_in_days
12
10
 
13
- from hestia_earth.models.utils.blank_node import most_relevant_blank_node_by_id
14
- from . import _term_id, _include_method, flatten_args
15
- from .term import get_lookup_value
11
+ from . import flatten_args
12
+ from .blank_node import most_relevant_blank_node_by_id
13
+ from .method import include_methodModel
14
+ from .term import download_term, get_lookup_value
16
15
 
17
16
 
18
17
  # TODO: verify those values
@@ -29,8 +28,8 @@ MEASUREMENT_METHOD_CLASSIFICATIONS = [e.value for e in MeasurementMethodClassifi
29
28
 
30
29
  def _new_measurement(term, model=None):
31
30
  node = {'@type': SchemaType.MEASUREMENT.value}
32
- node['term'] = linked_node(term if isinstance(term, dict) else download_hestia(_term_id(term)))
33
- return _include_method(node, term_id=model)
31
+ node['term'] = linked_node(term if isinstance(term, dict) else download_term(term))
32
+ return include_methodModel(node, term_id=model)
34
33
 
35
34
 
36
35
  def measurement_value(measurement: dict, is_larger_unit: bool = False) -> float:
@@ -0,0 +1,20 @@
1
+ from typing import Union
2
+ from hestia_earth.schema import TermTermType
3
+ from hestia_earth.utils.model import linked_node
4
+
5
+ from .term import download_term
6
+
7
+
8
+ def include_method(node: dict, term_id: Union[None, str, dict], key='method'):
9
+ term = (
10
+ download_term(term_id, TermTermType.MODEL) or download_term(term_id) or {}
11
+ ) if isinstance(term_id, str) else term_id
12
+ return node | ({} if term is None or term.get('@id') is None else {key: linked_node(term)})
13
+
14
+
15
+ def include_model(node: dict, term_id: Union[None, str, dict]):
16
+ return include_method(node, term_id=term_id, key='model')
17
+
18
+
19
+ def include_methodModel(node: dict, term_id: Union[None, str, dict]):
20
+ return include_method(node, term_id=term_id, key='methodModel')
@@ -1,15 +1,14 @@
1
1
  from hestia_earth.schema import SchemaType
2
- from hestia_earth.utils.api import download_hestia
3
2
  from hestia_earth.utils.model import linked_node
4
3
 
5
- from . import _term_id, _include_model
6
- from .term import get_lookup_value
4
+ from .term import get_lookup_value, download_term
5
+ from .method import include_model
7
6
 
8
7
 
9
8
  def _new_practice(term, model=None):
10
9
  node = {'@type': SchemaType.PRACTICE.value}
11
- node['term'] = linked_node(term if isinstance(term, dict) else download_hestia(_term_id(term)))
12
- return _include_model(node, model)
10
+ node['term'] = linked_node(term if isinstance(term, dict) else download_term(term))
11
+ return include_model(node, model)
13
12
 
14
13
 
15
14
  def is_model_enabled(model: str, term_id: str, practice: dict = None):
@@ -1,26 +1,25 @@
1
1
  from hestia_earth.schema import SchemaType, TermTermType, UNIQUENESS_FIELDS
2
- from hestia_earth.utils.api import download_hestia
3
2
  from hestia_earth.utils.model import filter_list_term_type, find_term_match, linked_node
4
3
  from hestia_earth.utils.tools import flatten, list_sum, non_empty_list, get_dict_key
5
4
 
6
- from . import _term_id, _include_model
7
5
  from .blank_node import get_total_value, get_total_value_converted
8
6
  from .constant import Units
9
7
  from .currency import DEFAULT_CURRENCY
10
8
  from .property import _get_nitrogen_content, get_node_property_value
11
- from .term import get_rice_paddy_terms
9
+ from .term import get_rice_paddy_terms, download_term
10
+ from .method import include_model
12
11
 
13
12
 
14
13
  def _new_product(term, value: float = None, model=None):
15
14
  node = {'@type': SchemaType.PRODUCT.value}
16
- node['term'] = linked_node(term if isinstance(term, dict) else download_hestia(_term_id(term)))
15
+ node['term'] = linked_node(term if isinstance(term, dict) else download_term(term))
17
16
  if value is not None:
18
17
  node['value'] = [value]
19
18
  elif value == 0:
20
19
  node['economicValueShare'] = 0
21
20
  node['revenue'] = 0
22
21
  node['currency'] = DEFAULT_CURRENCY
23
- return _include_model(node, model)
22
+ return include_model(node, model)
24
23
 
25
24
 
26
25
  def _match_list_el(source: list, dest: list, key: str):
@@ -1,22 +1,17 @@
1
- from functools import cache
2
-
3
1
  from hestia_earth.schema import SchemaType, TermTermType
4
- from hestia_earth.utils.api import download_hestia
5
2
  from hestia_earth.utils.lookup import download_lookup, extract_grouped_data, get_table_value, column_name
6
3
  from hestia_earth.utils.model import find_term_match, linked_node
7
4
  from hestia_earth.utils.tools import list_sum, safe_parse_float
8
5
 
9
- from . import _term_id, _include_methodModel
10
- from .term import get_lookup_value
11
- from ..log import debugMissingLookup
12
-
13
- download_hestia_cached = cache(download_hestia)
6
+ from hestia_earth.models.log import debugMissingLookup
7
+ from .method import include_methodModel
8
+ from .term import download_term, get_lookup_value
14
9
 
15
10
 
16
11
  def _new_property(term, model=None):
17
12
  node = {'@type': SchemaType.PROPERTY.value}
18
- node['term'] = linked_node(term if isinstance(term, dict) else download_hestia(_term_id(term)))
19
- return _include_methodModel(node, model)
13
+ node['term'] = linked_node(term if isinstance(term, dict) else download_term(term, TermTermType.PROPERTY))
14
+ return include_methodModel(node, model)
20
15
 
21
16
 
22
17
  def merge_properties(properties: list, new_properties: list):
@@ -30,7 +25,7 @@ def get_property_lookup_value(model: str, term_id: str, column: str):
30
25
  return get_lookup_value(term, column, model=model, term=term_id)
31
26
 
32
27
 
33
- def find_term_property(term, property: str, default=None, keep_in_memory=False) -> dict:
28
+ def find_term_property(term, property: str, default=None) -> dict:
34
29
  """
35
30
  Get the property by `@id` linked to the `Term` in the glossary.
36
31
 
@@ -42,23 +37,20 @@ def find_term_property(term, property: str, default=None, keep_in_memory=False)
42
37
  The `term.@id` of the property. Example: `nitrogenContent`.
43
38
  default : Any
44
39
  The default value if the property is not found. Defaults to `None`.
45
- keep_in_memory: bool
46
- Should we cache results from download_hestia(). Default False
47
40
 
48
41
  Returns
49
42
  -------
50
43
  dict
51
44
  The property if found, `default` otherwise.
52
45
  """
53
- download_func = download_hestia_cached if keep_in_memory else download_hestia
54
46
  props = term.get('defaultProperties', []) if isinstance(term, dict) else []
55
- term_id = _term_id(term)
56
- props = (download_func(term_id) or {}).get('defaultProperties', []) if len(props) == 0 and term_id else props
47
+ props = (
48
+ download_term(term, TermTermType.PROPERTY) or {}
49
+ ).get('defaultProperties', []) if len(props) == 0 and term else props
57
50
  return find_term_match(props, property, default)
58
51
 
59
52
 
60
- def get_node_property(node: dict, property: str, find_default_property: bool = True,
61
- keep_in_memory: bool = False) -> dict:
53
+ def get_node_property(node: dict, property: str, find_default_property: bool = True) -> dict:
62
54
  """
63
55
  Get the property by `@id` linked to the Blank Node in the glossary.
64
56
 
@@ -74,8 +66,6 @@ def get_node_property(node: dict, property: str, find_default_property: bool = T
74
66
  The `term.@id` of the property. Example: `nitrogenContent`.
75
67
  find_default_property : bool
76
68
  Default to fetching the property from the `defaultProperties` of the `Term`.
77
- keep_in_memory:
78
- If True and find_default_property is True, will cache this term_id call to api
79
69
 
80
70
  Returns
81
71
  -------
@@ -83,7 +73,7 @@ def get_node_property(node: dict, property: str, find_default_property: bool = T
83
73
  The property if found, `None` otherwise.
84
74
  """
85
75
  prop = find_term_match(node.get('properties', []), property, None)
86
- return find_term_property(node.get('term', {}), property, {}, keep_in_memory) if all([
76
+ return find_term_property(node.get('term', {}), property, {}) if all([
87
77
  find_default_property,
88
78
  prop is None
89
79
  ]) else (prop or {})
@@ -115,7 +105,7 @@ def node_property_lookup_value(model: str, node_term: dict, prop_id: str, defaul
115
105
 
116
106
  def get_node_property_value(model: str, node: dict, prop_id: str, default=None, handle_percents=True, **log_args):
117
107
  prop = get_node_property(node, prop_id)
118
- term = (prop or {}).get('term', download_hestia(prop_id))
108
+ term = (prop or {}).get('term') or download_term(prop_id, TermTermType.PROPERTY)
119
109
  units = (term or {}).get('units')
120
110
  value = prop.get('value') if prop else node_property_lookup_value(model, node.get('term', {}), prop_id, **log_args)
121
111
  return default if value is None else (value / 100 if units == '%' and handle_percents else value)
@@ -1,11 +1,12 @@
1
+ from typing import Optional
2
+
1
3
  from hestia_earth.schema import SchemaType, SiteSiteType, TermTermType
2
4
  from hestia_earth.utils.api import find_related
3
- from hestia_earth.utils.lookup import download_lookup, get_table_value, column_name
4
5
  from hestia_earth.utils.tools import non_empty_list, flatten, safe_parse_date
5
6
 
6
- from hestia_earth.models.log import debugMissingLookup
7
7
  from . import cached_value, _load_calculated_node
8
8
  from .term import get_land_cover_siteTypes
9
+ from .lookup import get_region_lookup_value
9
10
 
10
11
  CACHE_YEARS_KEY = 'years'
11
12
  WATER_TYPES = [
@@ -67,7 +68,7 @@ def years_from_cycles(cycles: list):
67
68
  ] for cycle in cycles))))
68
69
 
69
70
 
70
- def related_cycles(site: dict):
71
+ def related_cycles(site: dict, cycles_mapping: Optional[dict[str, dict]] = None):
71
72
  """
72
73
  Get the list of `Cycle` related to the `Site`.
73
74
  Gets the `recalculated` data if available, else `original`.
@@ -76,15 +77,23 @@ def related_cycles(site: dict):
76
77
  ----------
77
78
  site_id : str
78
79
  The `@id` of the `Site`.
80
+ cycles_mapping : dict[str, dict], optional
81
+ An optional dict of related `Cycle`s for which the data has already been retrieved, with the format
82
+ `{cycle_id (str): cycle (dict), ...ids}`.
79
83
 
80
84
  Returns
81
85
  -------
82
86
  list[dict]
83
87
  The related `Cycle`s as `dict`.
84
88
  """
89
+ cycles_mapping = cycles_mapping or {}
90
+
85
91
  cached_nodes = [n for n in cached_value(site, 'related', []) if n.get('@type') == SchemaType.CYCLE.value]
86
92
  related_nodes = cached_nodes or find_related(SchemaType.SITE, site.get('@id'), SchemaType.CYCLE) or []
87
- return non_empty_list(map(lambda node: _load_calculated_node(node, SchemaType.CYCLE), related_nodes))
93
+ return non_empty_list(map(
94
+ lambda node: cycles_mapping.get(node['@id']) or _load_calculated_node(node, SchemaType.CYCLE),
95
+ related_nodes
96
+ ))
88
97
 
89
98
 
90
99
  def related_years(site: dict):
@@ -117,10 +126,7 @@ def valid_site_type(site: dict, site_types=[SiteSiteType.CROPLAND.value, SiteSit
117
126
 
118
127
 
119
128
  def region_factor(model: str, region_id: str, term_id: str, termType: TermTermType):
120
- lookup_name = f"region-{termType.value}.csv"
121
- value = get_table_value(download_lookup(lookup_name), 'termid', region_id, column_name(term_id))
122
- debugMissingLookup(lookup_name, 'termid', region_id, term_id, value, model=model, term=term_id)
123
- return value
129
+ return get_region_lookup_value(f"region-{termType.value}.csv", region_id, term_id, model=model, term=term_id)
124
130
 
125
131
 
126
132
  def get_land_cover_term_id(site_type: str):
@@ -1,5 +1,9 @@
1
+ from typing import Union
2
+ from functools import lru_cache
3
+ import json
1
4
  from hestia_earth.schema import SchemaType, TermTermType, SiteSiteType
2
- from hestia_earth.utils.api import find_node, search
5
+ from hestia_earth.utils.storage import _load_from_storage
6
+ from hestia_earth.utils.api import find_node, search, download_hestia
3
7
  from hestia_earth.utils.lookup import download_lookup, get_table_value, column_name
4
8
 
5
9
  from .constant import Units
@@ -20,6 +24,28 @@ def get_lookup_value(lookup_term: dict, column: str, skip_debug: bool = False, *
20
24
  return value
21
25
 
22
26
 
27
+ @lru_cache()
28
+ def _load_term_file(term_type: str):
29
+ try:
30
+ filepath = f"glossary/{term_type}.json"
31
+ nodes = json.loads(_load_from_storage(filepath, glossary=True))
32
+ return {node.get('@id'): node for node in nodes}
33
+ except Exception as e:
34
+ print(e)
35
+ return {}
36
+
37
+
38
+ def download_term(term: Union[str, dict], termType: Union[str, TermTermType] = None):
39
+ term_id = term.get('@id', term.get('id')) if isinstance(term, dict) else term
40
+ term_type = (
41
+ termType if isinstance(termType, str) else termType.value
42
+ ) if termType else (
43
+ term.get('termType') if isinstance(term, dict) else None
44
+ )
45
+ cached_nodes = _load_term_file(term_type) if term_type else {}
46
+ return cached_nodes.get(term_id) or download_hestia(term_id)
47
+
48
+
23
49
  def get_liquid_fuel_terms():
24
50
  """
25
51
  Find all "liquid" `fuel` terms from the Glossary:
@@ -1 +1 @@
1
- VERSION = '0.67.0'
1
+ VERSION = '0.68.0'
@@ -1,7 +1,5 @@
1
1
  import os
2
2
  import sys
3
- import platform
4
- import resource
5
3
  import logging
6
4
 
7
5
  LOG_LEVEL = os.getenv('LOG_LEVEL', 'INFO')
@@ -44,15 +42,6 @@ if LOG_FILENAME is not None:
44
42
  def _join_args(**kwargs): return ', '.join([f"{key}={value}" for key, value in kwargs.items()])
45
43
 
46
44
 
47
- def log_memory_usage(**kwargs):
48
- factor = 1024 * (
49
- 1024 if platform.system() in ['Darwin', 'Windows'] else 1
50
- )
51
- value = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / factor
52
- extra = (', ' + _join_args(**kwargs)) if len(kwargs.keys()) > 0 else ''
53
- logger.info('memory used=%s, unit=MB' + extra, value)
54
-
55
-
56
45
  def _log_node_suffix(node: dict = {}):
57
46
  node_type = node.get('@type', node.get('type')) if node else None
58
47
  node_id = node.get('@id', node.get('id', node.get('term', {}).get('@id'))) if node else None
@@ -1,18 +1,27 @@
1
1
  import os
2
+ import resource
3
+ import platform
2
4
  from typing import Union, List
3
5
  import importlib
4
6
  from functools import reduce
5
7
  import concurrent.futures
6
8
  from copy import deepcopy
7
- from hestia_earth.utils.tools import non_empty_list
9
+ from hestia_earth.utils.tools import non_empty_list, current_time_ms
8
10
 
9
11
  from hestia_earth.models.version import VERSION
10
- from ..log import logger, log_memory_usage
12
+ from ..log import logger
11
13
  from ..utils import get_required_model_param, _snakecase
12
14
  from ..strategies.run import should_run
13
15
  from ..strategies.merge import merge
14
16
 
15
17
 
18
+ def _memory_usage():
19
+ factor = 1024 * (
20
+ 1024 if platform.system() in ['Darwin', 'Windows'] else 1
21
+ )
22
+ return resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / factor
23
+
24
+
16
25
  def _max_workers(type: str):
17
26
  try:
18
27
  return int(os.getenv(f"MAX_WORKERS_{type.upper()}"))
@@ -78,13 +87,17 @@ def _run_post_checks(data: dict):
78
87
  def _run_model(data: dict, model: dict, all_models: list):
79
88
  model_id = get_required_model_param(model, 'model')
80
89
  model_value = model.get('value') or _list_except_item(all_models, model)
81
- log_memory_usage(model_model=model_id, model_value=model_value, step='before')
90
+
91
+ now = current_time_ms()
92
+ memory_usage = _memory_usage()
82
93
 
83
94
  module = _import_model(model_id.replace('-', '_'))
84
95
  # if no value is provided, use all the models but this one
85
96
  result = module.get('run')(model_value, data)
86
97
 
87
- log_memory_usage(model_model=model_id, model_value=model_value, step='after')
98
+ logger.info('model_model=%s, model_value=%s, time=%s, memory_used=%s',
99
+ model_id, model.get('value'), current_time_ms() - now, _memory_usage() - memory_usage)
100
+
88
101
  return {'data': data, 'model': model, 'version': module.get('version'), 'result': result}
89
102
 
90
103
 
@@ -1,29 +1,11 @@
1
- from hestia_earth.utils.lookup import download_lookup, get_table_value, column_name
2
- from hestia_earth.utils.api import download_hestia
1
+ from hestia_earth.utils.lookup_utils import is_node_type_allowed
3
2
 
4
3
  from hestia_earth.orchestrator.log import debugValues, logShouldRun
5
4
  from hestia_earth.orchestrator.utils import get_required_model_param, find_term_match
6
5
 
7
- _ALLOW_ALL = 'all'
8
-
9
-
10
- def _lookup_values(term: dict, column: str):
11
- term_id = term.get('@id')
12
- term_type = term.get('termType')
13
- lookup = download_lookup(f"{term_type}.csv")
14
- values = get_table_value(lookup, 'termid', term_id, column_name(column))
15
- return (values or _ALLOW_ALL).split(';')
16
-
17
-
18
- def _is_node_type_allowed(data: dict, term_id: str):
19
- node_type = data.get('@type', data.get('type'))
20
- term = download_hestia(term_id)
21
- allowed_types = _lookup_values(term, 'typesAllowed') if term else [_ALLOW_ALL]
22
- return True if _ALLOW_ALL in allowed_types or not node_type else node_type in allowed_types
23
-
24
6
 
25
7
  def _run_required(data: dict, model: str, term_id: str):
26
- node_type_allowed = _is_node_type_allowed(data, term_id)
8
+ node_type_allowed = is_node_type_allowed(data, term_id)
27
9
 
28
10
  run_required = all([node_type_allowed])
29
11
  debugValues(data, model=model, term=term_id,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: hestia-earth-models
3
- Version: 0.67.0
3
+ Version: 0.68.0
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
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python :: 3.6
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
14
  Requires-Dist: hestia-earth-schema==30.*
15
- Requires-Dist: hestia-earth-utils>=0.13.19
15
+ Requires-Dist: hestia-earth-utils>=0.14.0
16
16
  Requires-Dist: python-dateutil>=2.8.1
17
17
  Requires-Dist: CurrencyConverter==0.16.8
18
18
  Requires-Dist: haversine>=2.7.0