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
@@ -185,7 +185,7 @@ def _gap_filled_start_date(land_cover_id: str, end_date: str, cycle: dict) -> di
185
185
  cycle_duration = _get_cycle_duration(cycle, land_cover_id)
186
186
  return {
187
187
  "startDate": max(
188
- _gap_filled_date_obj(end_date) - timedelta(days=cycle_duration)
188
+ _gap_filled_date_obj(end_date) - timedelta(days=cycle_duration - 1)
189
189
  if cycle_duration else datetime.fromtimestamp(0),
190
190
  _gap_filled_date_obj(cycle.get("startDate"), mode=DatestrGapfillMode.START)
191
191
  if cycle.get("startDate") else datetime.fromtimestamp(0)
@@ -2,13 +2,14 @@ from os.path import dirname, abspath
2
2
  import sys
3
3
 
4
4
  from hestia_earth.models.utils import _run_in_serie
5
- from . import cache
5
+ from . import cache, country
6
6
 
7
7
  CURRENT_DIR = dirname(abspath(__file__)) + '/'
8
8
  sys.path.append(CURRENT_DIR)
9
9
 
10
10
  MODELS = [
11
- cache.run
11
+ cache.run,
12
+ country.run
12
13
  ]
13
14
 
14
15
 
@@ -0,0 +1,9 @@
1
+ """
2
+ Post Checks Country
3
+
4
+ Stores a simpler version of the country as originally set on the Site.
5
+ """
6
+ from hestia_earth.utils.model import linked_node
7
+
8
+
9
+ def run(site: dict): return site | {'country': linked_node(site.get('country'))}
@@ -2,7 +2,7 @@ from os.path import dirname, abspath
2
2
  import sys
3
3
 
4
4
  from hestia_earth.models.utils import _run_in_serie
5
- from . import cache_years, cache_geospatialDatabase, cache_sources
5
+ from . import cache_years, cache_geospatialDatabase, cache_sources, country
6
6
 
7
7
  CURRENT_DIR = dirname(abspath(__file__)) + '/'
8
8
  sys.path.append(CURRENT_DIR)
@@ -10,7 +10,8 @@ sys.path.append(CURRENT_DIR)
10
10
  MODELS = [
11
11
  cache_years.run,
12
12
  cache_geospatialDatabase.run,
13
- cache_sources.run
13
+ cache_sources.run,
14
+ country.run
14
15
  ]
15
16
 
16
17
 
@@ -0,0 +1,9 @@
1
+ """
2
+ Pre Checks Country
3
+
4
+ Load the complete country data from HESTIA to be able to use `subClassOf` for example.
5
+ """
6
+ from hestia_earth.utils.api import download_hestia
7
+
8
+
9
+ def run(site: dict): return site | {'country': download_hestia(site.get('country', {}).get('@id'))}
@@ -8,10 +8,9 @@ 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, Union
11
+ from typing import Any, Callable
12
12
  from hestia_earth.schema import SchemaType
13
13
  from hestia_earth.utils.api import download_hestia
14
- from hestia_earth.utils.model import linked_node
15
14
  from hestia_earth.utils.tools import flatten, non_empty_list
16
15
  from hestia_earth.utils.date import is_in_days, is_in_months
17
16
 
@@ -44,20 +43,6 @@ def _omit(values: dict, keys: list) -> dict: return {k: v for k, v in values.ite
44
43
  def _include(value: dict, keys: list) -> dict: return {k: v for k, v in value.items() if k in keys}
45
44
 
46
45
 
47
- def _include_model(node: dict, term_id: str):
48
- term = download_hestia(term_id) or {}
49
- return {**node, **({} if term.get('@id') is None else {'model': linked_node(term)})}
50
-
51
-
52
- def _include_method(node: dict, term_id: Union[None, str, dict], key='method'):
53
- term = (download_hestia(term_id) or {}) if isinstance(term_id, str) else term_id
54
- return node | ({} if term is None or term.get('@id') is None else {key: linked_node(term)})
55
-
56
-
57
- def _include_methodModel(node: dict, term_id: str):
58
- return _include_method(node, term_id=term_id, key='methodModel')
59
-
60
-
61
46
  def _run_in_serie(data: dict, models: list): return reduce(lambda prev, model: model(prev), models, data)
62
47
 
63
48
 
@@ -12,38 +12,48 @@ from typing import (
12
12
  Optional,
13
13
  Union
14
14
  )
15
-
16
15
  from dateutil import parser
17
16
  from dateutil.relativedelta import relativedelta
18
17
  from hestia_earth.schema import TermTermType
19
- from hestia_earth.utils.api import download_hestia
20
18
  from hestia_earth.utils.blank_node import ArrayTreatment, get_node_value
21
19
  from hestia_earth.utils.model import filter_list_term_type
22
20
  from hestia_earth.utils.tools import (
23
21
  flatten,
24
22
  list_sum,
23
+ list_average,
25
24
  safe_parse_date,
26
25
  safe_parse_float,
27
26
  non_empty_list
28
27
  )
29
-
30
- from . import is_from_model, _filter_list_term_unit, is_iterable, full_date_str
31
- from .constant import Units, get_atomic_conversion
32
- from .lookup import (
28
+ from hestia_earth.utils.lookup_utils import (
33
29
  is_model_siteType_allowed,
34
30
  is_siteType_allowed,
35
- is_product_id_allowed, is_product_termType_allowed,
36
- is_input_id_allowed, is_input_termType_allowed, _node_value
31
+ is_product_id_allowed,
32
+ is_product_termType_allowed,
33
+ is_input_id_allowed,
34
+ is_input_termType_allowed
37
35
  )
36
+
37
+ from hestia_earth.models.log import debugValues, log_as_table
38
+ from . import is_from_model, _filter_list_term_unit, is_iterable, full_date_str
39
+ from .constant import Units, get_atomic_conversion
40
+ from .lookup import _node_value
38
41
  from .property import get_node_property, get_node_property_value
39
- from .term import get_lookup_value
40
- from ..log import debugValues, log_as_table
42
+ from .term import get_lookup_value, download_term
41
43
 
42
44
  # TODO: verify those values
43
45
  MAX_DEPTH = 1000
44
46
  OLDEST_DATE = '1800'
45
47
 
46
48
 
49
+ def group_by_term(values: list):
50
+ def group_by(groups: dict, value: dict):
51
+ key = value.get('term', {}).get('@id')
52
+ groups[key] = groups.get(key, []) + [value]
53
+ return groups
54
+ return reduce(group_by, values, {})
55
+
56
+
47
57
  def merge_blank_nodes(source: list, new_values: list):
48
58
  """
49
59
  Merge a list of blank nodes into an existing list of blank nodes.
@@ -114,29 +124,29 @@ def _module_term_id(term_id: str, module):
114
124
  return getattr(module, 'TERM_ID', term_id_str).split(',')[0]
115
125
 
116
126
 
117
- def _run_model_required(model: str, term: dict, data: dict):
118
- siteType_allowed = is_model_siteType_allowed(model, term, data)
127
+ def _run_model_required(model: str, term_id: str, data: dict):
128
+ siteType_allowed = is_model_siteType_allowed(model, term_id, data)
119
129
 
120
130
  run_required = all([siteType_allowed])
121
- debugValues(data, model=model, term=term.get('@id'),
131
+ debugValues(data, model=model, term=term_id,
122
132
  run_required=run_required,
123
133
  siteType_allowed=siteType_allowed)
124
134
  return run_required
125
135
 
126
136
 
127
- def _run_required(model: str, term: dict, data: dict):
128
- siteType_allowed = is_siteType_allowed(data, term)
129
- product_id_allowed = is_product_id_allowed(data, term)
130
- product_termType_allowed = is_product_termType_allowed(data, term)
131
- input_id_allowed = is_input_id_allowed(data, term)
132
- input_termType_allowed = is_input_termType_allowed(data, term)
137
+ def _run_required(model: str, term_id: str, data: dict):
138
+ siteType_allowed = is_siteType_allowed(data, term_id)
139
+ product_id_allowed = is_product_id_allowed(data, term_id)
140
+ product_termType_allowed = is_product_termType_allowed(data, term_id)
141
+ input_id_allowed = is_input_id_allowed(data, term_id)
142
+ input_termType_allowed = is_input_termType_allowed(data, term_id)
133
143
 
134
144
  run_required = all([
135
145
  siteType_allowed, product_id_allowed, product_termType_allowed, input_id_allowed, input_termType_allowed
136
146
  ])
137
147
  # model is only used for logs here, skip logs if model not provided
138
148
  if model:
139
- debugValues(data, model=model, term=term.get('@id'),
149
+ debugValues(data, model=model, term=term_id,
140
150
  siteType_allowed=siteType_allowed,
141
151
  product_id_allowed=product_id_allowed,
142
152
  product_termType_allowed=product_termType_allowed,
@@ -144,7 +154,7 @@ def _run_required(model: str, term: dict, data: dict):
144
154
  input_termType_allowed=input_termType_allowed)
145
155
  # logging this for the model would cause issues parsing statuses
146
156
  if model != 'emissionNotRelevant':
147
- debugValues(data, model=model, term=term.get('@id'), run_required=run_required)
157
+ debugValues(data, model=model, term=term_id, run_required=run_required)
148
158
  return run_required
149
159
 
150
160
 
@@ -166,10 +176,9 @@ def is_run_required(model: str, term_id: str, node: dict):
166
176
  bool
167
177
  True if the model is required to run.
168
178
  """
169
- term = download_hestia(term_id)
170
179
  return (
171
- (_run_model_required(model, term, node) if model else True) and _run_required(model, term, node)
172
- ) if term else True
180
+ (_run_model_required(model, term_id, node) if model else True) and _run_required(model, term_id, node)
181
+ ) if term_id else True
173
182
 
174
183
 
175
184
  def run_if_required(model: str, term_id: str, data: dict, module):
@@ -1271,7 +1280,7 @@ def get_inputs_from_properties(input: dict, term_types: Union[TermTermType, List
1271
1280
  properties = (
1272
1281
  input.get('properties') or
1273
1282
  input.get('term', {}).get('defaultProperties') or
1274
- download_hestia(input.get('term', {}).get('@id')).get('defaultProperties')
1283
+ download_term(input.get('term', {})).get('defaultProperties')
1275
1284
  )
1276
1285
  inputs = non_empty_list([
1277
1286
  {
@@ -1335,11 +1344,14 @@ def _node_from_group(nodes: list):
1335
1344
  # `nodes` contain list with consecutive dates
1336
1345
  return nodes[0] if len(nodes) == 1 else (
1337
1346
  # if all nodes have the same dates, sum up the values
1338
- nodes[0] | {'value': _sum_nodes_value(nodes)} if _same_dates(nodes)
1339
- else nodes[0] | {
1340
- 'startDate': min(n.get('startDate') for n in nodes),
1341
- 'endDate': max(n.get('endDate') for n in nodes)
1342
- }
1347
+ nodes[0] | (
1348
+ {
1349
+ 'value': _sum_nodes_value(nodes)
1350
+ } if _same_dates(nodes) else {
1351
+ 'startDate': min(n.get('startDate') for n in nodes),
1352
+ 'endDate': max(n.get('endDate') for n in nodes)
1353
+ }
1354
+ )
1343
1355
  )
1344
1356
 
1345
1357
 
@@ -1349,7 +1361,7 @@ def _condense_nodes(nodes: list):
1349
1361
  return flatten(map(_node_from_group, grouped_nodes))
1350
1362
 
1351
1363
 
1352
- def _group_nodes_to_condense(nodes: list) -> dict:
1364
+ def _group_nodes_by_value_and_properties(nodes: list) -> dict:
1353
1365
  def _group_node(group: dict, node: dict):
1354
1366
  value = node.get('value', [])
1355
1367
  value = '-'.join(map(str, value if isinstance(value, list) else [value]))
@@ -1359,7 +1371,6 @@ def _group_nodes_to_condense(nodes: list) -> dict:
1359
1371
  f"{p.get('value')}"
1360
1372
  ])) for p in node.get('properties', [])
1361
1373
  ]))
1362
- # group by term, value, and properties
1363
1374
  group_key = '-'.join(non_empty_list([
1364
1375
  node.get('term', {}).get('@id', ''),
1365
1376
  value,
@@ -1371,8 +1382,51 @@ def _group_nodes_to_condense(nodes: list) -> dict:
1371
1382
  return reduce(_group_node, nodes, {})
1372
1383
 
1373
1384
 
1385
+ def _group_nodes_by_dates(nodes: list) -> dict:
1386
+ def _group_node(group: dict, node: dict):
1387
+ group_key = '-'.join(non_empty_list([
1388
+ node.get('term', {}).get('@id', ''),
1389
+ node.get('startDate'),
1390
+ node.get('endDate'),
1391
+ ]))
1392
+ group[group_key] = group.get(group_key, []) + [node]
1393
+ return group
1394
+
1395
+ return reduce(_group_node, nodes, {})
1396
+
1397
+
1398
+ def _average_properties(properties: list):
1399
+ # group properties by term
1400
+ grouped_properties = group_by_term(properties)
1401
+ return [
1402
+ props[0] | {
1403
+ 'value': list_average(non_empty_list([p.get('value') for p in props]), default=props[0].get('value'))
1404
+ }
1405
+ for props in grouped_properties.values()
1406
+ ]
1407
+
1408
+
1409
+ def _merge_same_dates(nodes: list):
1410
+ # group by term, startDate and endDate
1411
+ grouped_nodes = _group_nodes_by_dates(nodes)
1412
+
1413
+ def merge_nodes(nodes: list):
1414
+ properties = flatten([n.get('properties', []) for n in nodes])
1415
+ return nodes[0] | (
1416
+ {
1417
+ 'value': _sum_nodes_value(nodes)
1418
+ } | ({
1419
+ 'properties': _average_properties(properties)
1420
+ } if properties else {})
1421
+ ) if len(nodes) > 1 else nodes[0]
1422
+
1423
+ return list(map(merge_nodes, grouped_nodes.values()))
1424
+
1425
+
1374
1426
  def condense_nodes(nodes: list) -> list:
1375
- grouped_nodes = _group_nodes_to_condense(nodes)
1427
+ # merge nodes with the same term and dates as they need to be unique
1428
+ values = _merge_same_dates(nodes)
1429
+ grouped_nodes = _group_nodes_by_value_and_properties(values)
1376
1430
  return flatten(map(_condense_nodes, grouped_nodes.values()))
1377
1431
 
1378
1432
 
@@ -1446,13 +1500,13 @@ def most_relevant_blank_node_by_id(nodes: list, term_id: str, date: str):
1446
1500
  PROPERTY_UNITS_CONVERSIONS = {
1447
1501
  Units.KG.value: {
1448
1502
  Units.MJ.value: [
1449
- 'energyContentHigherHeatingValue', # "kg" to "mj"
1503
+ 'energyContentLowerHeatingValue', # "kg" to "mj"
1450
1504
  ]
1451
1505
  },
1452
1506
  Units.M3.value: {
1453
1507
  Units.MJ.value: [
1454
1508
  'density', # "m3" to "kg"
1455
- 'energyContentHigherHeatingValue', # "kg" to "mj"
1509
+ 'energyContentLowerHeatingValue', # "kg" to "mj"
1456
1510
  ]
1457
1511
  }
1458
1512
  }
@@ -1506,7 +1560,6 @@ def convert_unit_properties(node_value: Union[int, float], node: dict, dest_unit
1506
1560
  Convert a number `node_value` belonging to a term `node`, to unit `to_units` by chaining multiple unit conversions
1507
1561
  together.
1508
1562
  Uses terms properties for the conversion.
1509
- Uses cached calls to download_hestia() internally for speedup
1510
1563
  Returns None if no conversion possible.
1511
1564
  """
1512
1565
  src_unit = node.get("units") or node.get('term', {}).get('units', "")
@@ -1,6 +1,7 @@
1
1
  from typing import Union
2
2
  from hestia_earth.schema import Completeness, TermTermType
3
- from hestia_earth.utils.api import download_hestia
3
+
4
+ from .term import download_term
4
5
 
5
6
  completeness_fields = Completeness().required
6
7
 
@@ -12,7 +13,7 @@ def _completeness_term_type(cycle: dict, term: Union[str, dict, TermTermType]):
12
13
 
13
14
 
14
15
  def _get_term_type_completeness(cycle: dict, term: Union[str, dict]):
15
- term = download_hestia(term) if isinstance(term, str) else term
16
+ term = download_term(term) if isinstance(term, str) else term
16
17
  term_type = term.get('termType') if term else None
17
18
  return cycle.get('completeness', {}).get(term_type, False)
18
19
 
@@ -1,9 +1,10 @@
1
1
  from hestia_earth.schema import CycleFunctionalUnit, SiteSiteType, TermTermType, AnimalReferencePeriod
2
2
  from hestia_earth.utils.model import filter_list_term_type, find_term_match, find_primary_product
3
3
  from hestia_earth.utils.tools import list_sum, safe_parse_float, safe_parse_date, non_empty_list
4
+ from hestia_earth.utils.lookup_utils import is_siteType_allowed
4
5
 
5
6
  from ..log import logRequirements, debugValues
6
- from .lookup import all_factor_value, is_siteType_allowed
7
+ from .lookup import all_factor_value
7
8
  from .term import get_lookup_value
8
9
  from .property import get_node_property
9
10
  from .completeness import _is_term_type_complete
@@ -344,7 +345,7 @@ def is_organic(cycle: dict):
344
345
  `True` if the `Cycle` is organic, `False` otherwise.
345
346
  """
346
347
  practices = filter_list_term_type(cycle.get('practices', []), TermTermType.STANDARDSLABELS)
347
- return any([get_lookup_value(p.get('term', {}), 'isOrganic') == 'organic' for p in practices])
348
+ return next((get_lookup_value(p.get('term', {}), 'isOrganic') == 'organic' for p in practices), False)
348
349
 
349
350
 
350
351
  def is_irrigated(cycle: dict, **log_ars):
@@ -463,9 +464,9 @@ def get_animals_by_period(cycle: dict, period: AnimalReferencePeriod = AnimalRef
463
464
  ]
464
465
 
465
466
 
466
- def get_allowed_sites(model: str, term_id: str, termType: TermTermType, cycle: dict):
467
+ def get_allowed_sites(model: str, term_id: str, cycle: dict):
467
468
  sites = non_empty_list([cycle.get('site', None)]) + cycle.get('otherSites', [])
468
- allowed_sites = [s for s in sites if is_siteType_allowed(s, {'@id': term_id, 'termType': termType.value})]
469
+ allowed_sites = [s for s in sites if is_siteType_allowed(s, term_id)]
469
470
  allowed_site_ids = non_empty_list([s.get('@id', s.get('id')) for s in allowed_sites])
470
471
  debugValues(cycle, model=model, term=term_id, site_ids=';'.join(allowed_site_ids))
471
472
  return allowed_sites
@@ -36,8 +36,8 @@ def get_eco_climate_zone_value(node: dict, as_enum: bool = False) -> Union[int,
36
36
  Parameters
37
37
  ----------
38
38
  node : dict
39
- A HESTIA [Site](https://www-staging.hestia.earth/schema/Site) or
40
- [Cycle](https://www-staging.hestia.earth/schema/Cycle).
39
+ A HESTIA [Site](https://hestia.earth/schema/Site) or
40
+ [Cycle](https://hestia.earth/schema/Cycle).
41
41
 
42
42
  Returns
43
43
  -------
@@ -1,22 +1,22 @@
1
1
  from collections.abc import Iterable
2
2
  from typing import Optional, Union
3
3
  from hestia_earth.schema import EmissionMethodTier, SchemaType, TermTermType
4
- from hestia_earth.utils.api import download_hestia
5
4
  from hestia_earth.utils.model import linked_node
6
5
 
7
6
 
8
- from . import _term_id, _include_methodModel, flatten_args
7
+ from . import flatten_args
8
+ from .term import download_term
9
9
  from .blank_node import find_terms_value
10
+ from .method import include_methodModel
10
11
  from .constant import Units, get_atomic_conversion
11
12
 
12
-
13
13
  EMISSION_METHOD_TIERS = [e.value for e in EmissionMethodTier]
14
14
 
15
15
 
16
16
  def _new_emission(term, model=None):
17
17
  node = {'@type': SchemaType.EMISSION.value}
18
- node['term'] = linked_node(term if isinstance(term, dict) else download_hestia(_term_id(term)))
19
- return _include_methodModel(node, model)
18
+ node['term'] = linked_node(term if isinstance(term, dict) else download_term(term, TermTermType.EMISSION))
19
+ return include_methodModel(node, model)
20
20
 
21
21
 
22
22
  def get_nh3_no3_nox_to_n(cycle: dict, nh3_term_id: str, no3_term_id: str, nox_term_id: str, allow_none: bool = False):
@@ -1,5 +1,5 @@
1
1
  from hestia_earth.utils.model import find_term_match
2
- from hestia_earth.utils.lookup import download_lookup, get_table_value, column_name
2
+ from hestia_earth.utils.lookup import download_lookup, get_table_value, column_name, lookup_term_ids
3
3
  from hestia_earth.utils.tools import non_empty_list, safe_parse_float
4
4
 
5
5
  from hestia_earth.models.log import logShouldRun
@@ -11,9 +11,9 @@ DRY_MATTER_TERM_ID = 'dryMatter'
11
11
 
12
12
  def get_feedipedia_properties():
13
13
  lookup = download_lookup('property.csv')
14
- term_ids = list(lookup.termid)
15
14
  term_ids = [
16
- term_id for term_id in term_ids if get_table_value(lookup, 'termid', term_id, column_name('feedipediaName'))
15
+ term_id for term_id in lookup_term_ids(lookup)
16
+ if get_table_value(lookup, 'termid', term_id, column_name('feedipediaName'))
17
17
  ]
18
18
  return term_ids
19
19
 
@@ -25,7 +25,6 @@ def _should_rescale_by_dm(property_id: str):
25
25
 
26
26
 
27
27
  def _dm_property(term_id: str, property_values: dict, dm_property_values: dict, dry_matter_property: dict):
28
- blank_node = _new_property(term_id)
29
28
  blank_node_data = {}
30
29
  for property_key in property_values.keys():
31
30
  new_dm_value = safe_parse_float(dry_matter_property.get(property_key))
@@ -37,7 +36,7 @@ def _dm_property(term_id: str, property_values: dict, dm_property_values: dict,
37
36
  2
38
37
  ) if _should_rescale_by_dm(term_id) else old_property_value
39
38
  blank_node_data[property_key] = new_value
40
- return (blank_node | blank_node_data) if blank_node_data else None
39
+ return (_new_property(term_id) | blank_node_data) if blank_node_data else None
41
40
 
42
41
 
43
42
  def _map_properties(lookup, term_id: str, column_prefix: str):
@@ -74,7 +73,8 @@ def rescale_properties_from_dryMatter(model: str, node: dict, blank_nodes: list)
74
73
  new_properties = non_empty_list([
75
74
  exec_property(blank_node, p, dry_matter_property) for p in properties if all([
76
75
  not find_term_match(all_properties, p),
77
- p != DRY_MATTER_TERM_ID
76
+ p != DRY_MATTER_TERM_ID,
77
+ dry_matter_property
78
78
  ])
79
79
  ])
80
80
  for prop in new_properties:
@@ -1,6 +1,5 @@
1
1
  from typing import Optional
2
2
  from hestia_earth.schema import TermTermType
3
- from hestia_earth.utils.lookup import download_lookup
4
3
  from hestia_earth.utils.model import find_term_match, filter_list_term_type
5
4
  from hestia_earth.utils.tools import list_sum, safe_parse_date
6
5
 
@@ -131,6 +130,7 @@ def impact_country_value(
131
130
  lookup: str,
132
131
  group_key: str = None,
133
132
  country_fallback: bool = False,
133
+ default_no_values=None
134
134
  ) -> float:
135
135
  """
136
136
  Calculate the value of the impact based on lookup factors and `site.country.@id`.
@@ -160,19 +160,19 @@ def impact_country_value(
160
160
  nodes = filter_list_term_type(impact.get('emissionsResourceUse', []), term_type)
161
161
 
162
162
  country_id = get_country_id(impact)
163
- country_id = fallback_country(country_id, [download_lookup(lookup)]) if country_fallback else country_id
163
+ country_id = fallback_country(country_id, [lookup]) if country_fallback else country_id
164
164
 
165
165
  values = list(map(_term_factor_value(model, term_id, lookup, country_id, group_key), nodes))
166
166
  debugValues(impact, model=model, term=term_id,
167
167
  values_used=log_as_table(values))
168
168
 
169
- all_with_factors = all([v.get('coefficient') is not None for v in values if v.get('value') is not None])
169
+ has_values = len(values) > 0
170
+ missing_values = set([v.get('id') for v in values if v.get('value') and v.get('coefficient') is None])
171
+ all_with_factors = not missing_values
170
172
  values = [float((v.get('value') or 0) * (v.get('coefficient') or 0)) for v in values]
171
173
 
172
174
  # fail if some factors are missing
173
- return None if not all_with_factors else (
174
- list_sum(values) if len(values) > 0 else None
175
- )
175
+ return None if not all_with_factors else (list_sum(values) if has_values else default_no_values)
176
176
 
177
177
 
178
178
  def impact_aware_value(model: str, term_id: str, impact: dict, lookup: str, group_key: str = None) -> float:
@@ -1,15 +1,17 @@
1
- from hestia_earth.schema import SchemaType
2
- from hestia_earth.utils.api import download_hestia
1
+ from hestia_earth.schema import SchemaType, TermTermType
3
2
  from hestia_earth.utils.model import linked_node
4
3
 
5
- from . import _term_id, _include_methodModel
4
+ from .method import include_methodModel
5
+ from .term import download_term
6
6
 
7
7
 
8
8
  def _new_indicator(term, model=None, land_cover_id: str = None, previous_land_cover_id: str = None):
9
9
  node = {'@type': SchemaType.INDICATOR.value}
10
- node['term'] = linked_node(term if isinstance(term, dict) else download_hestia(_term_id(term)))
10
+ node['term'] = linked_node(term if isinstance(term, dict) else download_term(
11
+ term, TermTermType.CHARACTERISEDINDICATOR)
12
+ )
11
13
  if land_cover_id:
12
- node['landCover'] = linked_node(download_hestia(land_cover_id))
14
+ node['landCover'] = linked_node(download_term(land_cover_id, TermTermType.LANDCOVER))
13
15
  if previous_land_cover_id:
14
- node['previousLandCover'] = linked_node(download_hestia(previous_land_cover_id))
15
- return _include_methodModel(node, model)
16
+ node['previousLandCover'] = linked_node(download_term(previous_land_cover_id, TermTermType.LANDCOVER))
17
+ return include_methodModel(node, model)
@@ -1,18 +1,18 @@
1
1
  from hestia_earth.schema import TermTermType
2
- from hestia_earth.utils.lookup import column_name, download_lookup, get_table_value, extract_grouped_data
2
+ from hestia_earth.utils.lookup import download_lookup, extract_grouped_data, lookup_term_ids
3
3
  from hestia_earth.utils.tools import safe_parse_float
4
4
  from hestia_earth.utils.model import filter_list_term_type
5
5
 
6
- from ..log import debugMissingLookup
7
6
  from .term import get_lookup_value
8
7
  from .fertiliser import get_fertilisers_from_inputs
8
+ from .lookup import get_region_lookup_value
9
9
 
10
10
  BREAKDOWN_LOOKUP = 'region-inorganicFertiliser-fertGroupingNitrogen-breakdown.csv'
11
11
 
12
12
 
13
13
  def get_terms():
14
14
  lookup = download_lookup('inorganicFertiliser.csv', True)
15
- return list(lookup.termid)
15
+ return lookup_term_ids(lookup)
16
16
 
17
17
 
18
18
  def get_term_lookup(term_id: str, col_name: str):
@@ -35,9 +35,7 @@ def get_NH3_emission_factor(term_id: str, soilPh: float, temperature: float):
35
35
 
36
36
 
37
37
  def get_country_breakdown(model: str, term_id: str, country_id: str, col_name: str):
38
- lookup = download_lookup(BREAKDOWN_LOOKUP)
39
- value = get_table_value(lookup, 'termid', country_id, column_name(col_name))
40
- debugMissingLookup(BREAKDOWN_LOOKUP, 'termid', country_id, col_name, value, model=model, term=term_id)
38
+ value = get_region_lookup_value(BREAKDOWN_LOOKUP, country_id, col_name, model=model, term=term_id)
41
39
  return safe_parse_float(value, None)
42
40
 
43
41
 
@@ -1,19 +1,20 @@
1
1
  from hestia_earth.schema import SchemaType, TermTermType
2
- from hestia_earth.utils.api import download_hestia
3
2
  from hestia_earth.utils.model import find_term_match, linked_node, filter_list_term_type
4
3
  from hestia_earth.utils.tools import list_sum, non_empty_list, list_average, flatten
5
4
  from hestia_earth.utils.lookup import download_lookup, get_table_value, column_name
6
5
 
7
- from ..log import logger
8
- from . import _term_id, _include_model, _filter_list_term_unit, _load_calculated_node
6
+ from hestia_earth.models.log import logger
7
+ from . import _filter_list_term_unit, _load_calculated_node
9
8
  from .constant import Units
10
9
  from .blank_node import get_total_value, get_total_value_converted, get_lookup_value
10
+ from .term import download_term
11
+ from .method import include_model
11
12
 
12
13
 
13
14
  def _new_input(term, model=None):
14
15
  node = {'@type': SchemaType.INPUT.value}
15
- node['term'] = linked_node(term if isinstance(term, dict) else download_hestia(_term_id(term)))
16
- return _include_model(node, model)
16
+ node['term'] = linked_node(term if isinstance(term, dict) else download_term(term))
17
+ return include_model(node, model)
17
18
 
18
19
 
19
20
  def load_impacts(inputs: list):