hestia-earth-models 0.59.4__py3-none-any.whl → 0.59.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (55) hide show
  1. hestia_earth/models/cycle/animal/milkYield.py +86 -0
  2. hestia_earth/models/cycle/endDate.py +50 -0
  3. hestia_earth/models/cycle/inorganicFertiliser.py +3 -2
  4. hestia_earth/models/cycle/liveAnimal.py +3 -0
  5. hestia_earth/models/cycle/milkYield.py +8 -3
  6. hestia_earth/models/cycle/pre_checks/__init__.py +1 -2
  7. hestia_earth/models/cycle/startDate.py +42 -0
  8. hestia_earth/models/cycle/utils.py +1 -1
  9. hestia_earth/models/faostat2018/liveweightPerHead.py +77 -41
  10. hestia_earth/models/faostat2018/product/price.py +30 -55
  11. hestia_earth/models/faostat2018/utils.py +10 -2
  12. hestia_earth/models/geospatialDatabase/potentialEvapotranspirationLongTermAnnualMean.py +2 -2
  13. hestia_earth/models/geospatialDatabase/potentialEvapotranspirationMonthly.py +9 -8
  14. hestia_earth/models/geospatialDatabase/precipitationMonthly.py +10 -8
  15. hestia_earth/models/geospatialDatabase/temperatureAnnual.py +2 -5
  16. hestia_earth/models/geospatialDatabase/temperatureLongTermAnnualMean.py +2 -3
  17. hestia_earth/models/geospatialDatabase/temperatureMonthly.py +8 -8
  18. hestia_earth/models/geospatialDatabase/utils.py +6 -1
  19. hestia_earth/models/haversineFormula/transport/distance.py +6 -3
  20. hestia_earth/models/ipcc2006/n2OToAirInorganicFertiliserIndirect.py +1 -1
  21. hestia_earth/models/ipcc2019/organicCarbonPerHa.py +89 -114
  22. hestia_earth/models/ipcc2019/pastureGrass.py +2 -1
  23. hestia_earth/models/linkedImpactAssessment/__init__.py +78 -43
  24. hestia_earth/models/mocking/search-results.json +244 -271
  25. hestia_earth/models/schmidt2007/h2SToAirWasteTreatment.py +58 -0
  26. hestia_earth/models/schmidt2007/n2OToAirWasteTreatmentDirect.py +58 -0
  27. hestia_earth/models/schmidt2007/nh3ToAirWasteTreatment.py +58 -0
  28. hestia_earth/models/site/management.py +107 -12
  29. hestia_earth/models/site/soilMeasurement.py +9 -9
  30. hestia_earth/models/utils/__init__.py +4 -1
  31. hestia_earth/models/utils/animalProduct.py +6 -4
  32. hestia_earth/models/utils/blank_node.py +6 -5
  33. hestia_earth/models/utils/product.py +9 -1
  34. hestia_earth/models/utils/term.py +0 -23
  35. hestia_earth/models/version.py +1 -1
  36. {hestia_earth_models-0.59.4.dist-info → hestia_earth_models-0.59.6.dist-info}/METADATA +1 -1
  37. {hestia_earth_models-0.59.4.dist-info → hestia_earth_models-0.59.6.dist-info}/RECORD +53 -43
  38. tests/models/cycle/animal/test_milkYield.py +43 -0
  39. tests/models/cycle/test_endDate.py +24 -0
  40. tests/models/cycle/test_startDate.py +22 -0
  41. tests/models/faostat2018/product/test_price.py +25 -45
  42. tests/models/faostat2018/test_liveweightPerHead.py +106 -42
  43. tests/models/ipcc2019/test_organicCarbonPerHa.py +12 -18
  44. tests/models/schmidt2007/test_h2SToAirWasteTreatment.py +45 -0
  45. tests/models/schmidt2007/test_n2OToAirWasteTreatmentDirect.py +45 -0
  46. tests/models/schmidt2007/test_nh3ToAirWasteTreatment.py +45 -0
  47. tests/models/site/test_management.py +24 -3
  48. tests/models/site/test_soilMeasurement.py +40 -21
  49. tests/models/utils/test_blank_node.py +71 -3
  50. tests/models/utils/test_term.py +1 -8
  51. hestia_earth/models/cycle/pre_checks/startDate.py +0 -52
  52. tests/models/cycle/pre_checks/test_startDate.py +0 -44
  53. {hestia_earth_models-0.59.4.dist-info → hestia_earth_models-0.59.6.dist-info}/LICENSE +0 -0
  54. {hestia_earth_models-0.59.4.dist-info → hestia_earth_models-0.59.6.dist-info}/WHEEL +0 -0
  55. {hestia_earth_models-0.59.4.dist-info → hestia_earth_models-0.59.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,86 @@
1
+ """
2
+ Milk Yield
3
+
4
+ This model gap-fills the practice "Milk yield per animal X (raw/FPCM)" (e.g. `Milk yield per cow (raw)`) when:
5
+ - the practice is added to the Cycle itself, and we can match it with the `term` of an Animal blank node;
6
+ - the Animal blank node Term has a lookup value of `kgDayMilkForFeedingOffspring`.
7
+ """
8
+ from hestia_earth.schema import TermTermType
9
+ from hestia_earth.utils.model import filter_list_term_type
10
+ from hestia_earth.utils.tools import non_empty_list, safe_parse_float
11
+
12
+ from hestia_earth.models.log import logShouldRun, logRequirements, log_blank_nodes_id
13
+ from hestia_earth.models.utils.term import get_lookup_value
14
+ from hestia_earth.models.utils.practice import _new_practice
15
+ from .. import MODEL
16
+
17
+ REQUIREMENTS = {
18
+ "Cycle": {
19
+ "animals": [{
20
+ "@type": "Animal",
21
+ "term.termType": "liveAnimal"
22
+ }],
23
+ "optional": {
24
+ "practices": [{
25
+ "@type": "Practice",
26
+ "term.termType": "animalManagement"
27
+ }]
28
+ }
29
+ }
30
+ }
31
+ RETURNS = {
32
+ "Animal": [{
33
+ "practices": [{
34
+ "@type": "Practice",
35
+ "term.termType": "animalManagement"
36
+ }]
37
+ }]
38
+ }
39
+ LOOKUPS = {
40
+ "liveAnimal": ["milkYieldPracticeTermIds", "kgDayMilkForFeedingOffspring"]
41
+ }
42
+
43
+ MODEL_KEY = 'milkYield'
44
+
45
+
46
+ def _default_practice(animal: dict, practice_term_id: str):
47
+ term = animal.get('term', {})
48
+ value = get_lookup_value(term, LOOKUPS['liveAnimal'][1], model=MODEL, model_key=MODEL_KEY)
49
+ return (_new_practice(practice_term_id) | {'value': [safe_parse_float(value)]}) if value else None
50
+
51
+
52
+ def _run(cycle: dict, animal: dict):
53
+ term = animal.get('term', {})
54
+ term_id = term.get('@id')
55
+ value = get_lookup_value(term, LOOKUPS['liveAnimal'][0], model=MODEL, model_key=MODEL_KEY)
56
+ practice_ids = non_empty_list((value or '').split(';'))
57
+ practices = non_empty_list(
58
+ [p for p in cycle.get('practices', []) if p.get('term', {}).get('@id') in practice_ids] or (
59
+ [_default_practice(animal, practice_ids[0])] if practice_ids else []
60
+ )
61
+ )
62
+
63
+ logRequirements(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
64
+ practice_ids=log_blank_nodes_id(practices))
65
+
66
+ for practice in practices:
67
+ logShouldRun(cycle, MODEL, practice.get('term', {}).get('@id'), True, model_key=MODEL_KEY)
68
+
69
+ return {
70
+ **animal,
71
+ 'practices': animal.get('practices', []) + practices
72
+ } if practices else None
73
+
74
+
75
+ def _should_run(cycle: dict):
76
+ animals = filter_list_term_type(cycle.get('animals', []), TermTermType.LIVEANIMAL)
77
+ has_animals = len(animals) > 0
78
+
79
+ should_run = all([has_animals])
80
+ logShouldRun(cycle, MODEL, None, should_run, model_key=MODEL_KEY)
81
+ return should_run, animals
82
+
83
+
84
+ def run(cycle: dict):
85
+ should_run, animals = _should_run(cycle)
86
+ return non_empty_list([_run(cycle, a) for a in animals]) if should_run else []
@@ -0,0 +1,50 @@
1
+ """
2
+ Start Date
3
+
4
+ This model updates the [Cycle endDate](https://hestia.earth/schema/Cycle#endDate) to be in the following format:
5
+ `YYYY-MM-DD`.
6
+ """
7
+ from hestia_earth.utils.date import is_in_days
8
+ from hestia_earth.utils.tools import non_empty_list
9
+
10
+ from hestia_earth.models.log import logRequirements, logShouldRun
11
+ from hestia_earth.models.utils import last_day_of_month
12
+ from . import MODEL
13
+
14
+ REQUIREMENTS = {
15
+ "Cycle": {
16
+ "endDate": ""
17
+ }
18
+ }
19
+ RETURNS = {
20
+ "The endDate as a string": ""
21
+ }
22
+ MODEL_KEY = 'endDate'
23
+
24
+
25
+ def _last_day(date: str):
26
+ last_day = last_day_of_month(date[0:4], date[5:7])
27
+ return str(last_day.day)
28
+
29
+
30
+ def _run(cycle: dict):
31
+ value = cycle.get('endDate')
32
+ month = '12' if len(value) == 4 else ''
33
+ day = '31' if len(value) == 4 else _last_day(value)
34
+ return '-'.join(non_empty_list([value, month, day]))
35
+
36
+
37
+ def _should_run(cycle: dict):
38
+ has_endDate = cycle.get('endDate') is not None
39
+ has_incorrect_format = has_endDate and not is_in_days(cycle.get('endDate'))
40
+
41
+ logRequirements(cycle, model=MODEL, key=MODEL_KEY,
42
+ has_endDate=has_endDate,
43
+ has_incorrect_format=has_incorrect_format)
44
+
45
+ should_run = all([has_endDate, has_incorrect_format])
46
+ logShouldRun(cycle, MODEL, None, should_run, key=MODEL_KEY)
47
+ return should_run
48
+
49
+
50
+ def run(cycle: dict): return _run(cycle) if _should_run(cycle) else None
@@ -34,7 +34,7 @@ RETURNS = {
34
34
  }
35
35
  LOOKUPS = {
36
36
  "inorganicFertiliser": [
37
- "mustIncludeId",
37
+ "complementaryTermIds",
38
38
  "nitrogenContent", "nitrogenContent-min", "nitrogenContent-max",
39
39
  "phosphateContentAsP2O5", "phosphateContentAsP2O5-min", "phosphateContentAsP2O5-max",
40
40
  "potassiumContentAsK2O", "potassiumContentAsK2O-min", "potassiumContentAsK2O-max"
@@ -75,7 +75,8 @@ def _input(term_id: str, value: float, min: float = None, max: float = None):
75
75
  return input
76
76
 
77
77
 
78
- def _include_term_ids(term_id: str): return non_empty_list((get_term_lookup(term_id, 'mustIncludeId') or '').split(';'))
78
+ def _include_term_ids(term_id: str):
79
+ return non_empty_list((get_term_lookup(term_id, LOOKUPS['inorganicFertiliser'][0]) or '').split(';'))
79
80
 
80
81
 
81
82
  def _run_input(cycle: dict, input: dict):
@@ -43,6 +43,9 @@ RETURNS = {
43
43
  "value": ""
44
44
  }]
45
45
  }
46
+ LOOKUPS = {
47
+ "animalProduct": "liveAnimalTermId"
48
+ }
46
49
  MODEL_KEY = 'liveAnimal'
47
50
  VALID_SITE_TYPES = [SiteSiteType.ANIMAL_HOUSING.value, SiteSiteType.PERMANENT_PASTURE.value]
48
51
 
@@ -48,7 +48,7 @@ RETURNS = {
48
48
  }]
49
49
  }
50
50
  LOOKUPS = {
51
- "animalProduct": ["liveAnimal", "milkYieldPracticeId"]
51
+ "animalProduct": ["liveAnimalTermId", "milkYieldPracticeTermId"]
52
52
  }
53
53
 
54
54
  MODEL_KEY = 'milkYield'
@@ -74,11 +74,16 @@ def practice(term_id: str, value: float, properties: list, sd: float = None, min
74
74
  return data
75
75
 
76
76
 
77
+ def _practice_id(term: dict):
78
+ value = get_lookup_value(term, LOOKUPS['animalProduct'][1], model=MODEL, model_key=MODEL_KEY)
79
+ return value.split(';')[0] if value else None
80
+
81
+
77
82
  def _run(cycle: dict, product: dict):
78
83
  cycleDuration = cycle.get('cycleDuration')
79
84
 
80
85
  term = product.get('term', {})
81
- practice_id = get_lookup_value(term, 'milkYieldPracticeId')
86
+ practice_id = _practice_id(term)
82
87
 
83
88
  live_animal_term_id = _get_liveAnimal_term_id(product, model_key=MODEL_KEY)
84
89
  live_animal_node = find_term_match(cycle.get('animals', []), live_animal_term_id)
@@ -114,7 +119,7 @@ def _should_run_product(cycle: dict, product: dict):
114
119
  live_animal_node = find_term_match(cycle.get('animals', []), live_animal_term_id)
115
120
  has_live_animal_node_value = live_animal_node.get('value', 0) > 0
116
121
 
117
- practice_id = get_lookup_value(term, 'milkYieldPracticeId', model=MODEL, model_key=MODEL_KEY)
122
+ practice_id = _practice_id(term)
118
123
  has_practice_id = any([
119
124
  find_term_match(cycle.get('practices', []), practice_id),
120
125
  find_term_match((live_animal_node or {}).get('practices', []), practice_id)
@@ -2,14 +2,13 @@ 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 site, startDate, cache_sources
5
+ from . import site, cache_sources
6
6
 
7
7
  CURRENT_DIR = dirname(abspath(__file__)) + '/'
8
8
  sys.path.append(CURRENT_DIR)
9
9
 
10
10
  MODELS = [
11
11
  site.run,
12
- startDate.run,
13
12
  cache_sources.run
14
13
  ]
15
14
 
@@ -0,0 +1,42 @@
1
+ """
2
+ Start Date
3
+
4
+ This model updates the [Cycle startDate](https://hestia.earth/schema/Cycle#startDate) to be in the following format:
5
+ `YYYY-MM-DD`.
6
+ """
7
+ from hestia_earth.utils.date import is_in_days
8
+ from hestia_earth.utils.tools import non_empty_list
9
+
10
+ from hestia_earth.models.log import logRequirements, logShouldRun
11
+ from . import MODEL
12
+
13
+ REQUIREMENTS = {
14
+ "Cycle": {
15
+ "startDate": ""
16
+ }
17
+ }
18
+ RETURNS = {
19
+ "The startDate as a string": ""
20
+ }
21
+ MODEL_KEY = 'startDate'
22
+
23
+
24
+ def _run(cycle: dict):
25
+ value = cycle.get('startDate')
26
+ return '-'.join(non_empty_list([value, '01' if len(value) == 4 else '', '01']))
27
+
28
+
29
+ def _should_run(cycle: dict):
30
+ has_startDate = cycle.get('startDate') is not None
31
+ has_incorrect_format = has_startDate and not is_in_days(cycle.get('startDate'))
32
+
33
+ logRequirements(cycle, model=MODEL, key=MODEL_KEY,
34
+ has_startDate=has_startDate,
35
+ has_incorrect_format=has_incorrect_format)
36
+
37
+ should_run = all([has_startDate, has_incorrect_format])
38
+ logShouldRun(cycle, MODEL, None, should_run, key=MODEL_KEY)
39
+ return should_run
40
+
41
+
42
+ def run(cycle: dict): return _run(cycle) if _should_run(cycle) else None
@@ -6,7 +6,7 @@ from . import MODEL
6
6
 
7
7
 
8
8
  def _get_liveAnimal_term_id(product: dict, **log_ars):
9
- term_id = get_lookup_value(product.get('term', {}), 'liveAnimal', model=MODEL, **log_ars)
9
+ term_id = get_lookup_value(product.get('term', {}), 'liveAnimalTermId', model=MODEL, **log_ars)
10
10
  return term_id.split(';')[0] if term_id else None
11
11
 
12
12
 
@@ -1,20 +1,25 @@
1
1
  from hestia_earth.schema import TermTermType
2
+ from hestia_earth.utils.api import download_hestia
3
+ from hestia_earth.utils.lookup import get_table_value, column_name, download_lookup, extract_grouped_data_closest_date
2
4
  from hestia_earth.utils.model import filter_list_term_type
3
- from hestia_earth.utils.tools import non_empty_list, safe_parse_date
5
+ from hestia_earth.utils.tools import non_empty_list, safe_parse_date, safe_parse_float
4
6
 
5
- from hestia_earth.models.log import logRequirements, logShouldRun
6
- from hestia_earth.models.utils import _filter_list_term_unit
7
+ from hestia_earth.models.log import logRequirements, logShouldRun, debugMissingLookup
7
8
  from hestia_earth.models.utils.constant import Units
8
9
  from hestia_earth.models.utils.property import _new_property, node_has_no_property
10
+ from hestia_earth.models.utils.product import convert_product_to_unit
11
+ from hestia_earth.models.utils.animalProduct import FAO_LOOKUP_COLUMN, get_animalProduct_lookup_value
12
+ from .utils import get_liveAnimal_to_animalProduct_id
9
13
  from . import MODEL
10
- from .utils import product_equivalent_value
11
14
 
12
15
  REQUIREMENTS = {
13
16
  "Cycle": {
14
17
  "endDate": "",
15
- "products": [
16
- {"@type": "Product", "value": "", "term.termType": "animalProduct", "term.units": "kg liveweight"}
17
- ],
18
+ "products": [{
19
+ "@type": "Product",
20
+ "value": "",
21
+ "term.termType": ["animalProduct", "liveAnimal"]
22
+ }],
18
23
  "site": {
19
24
  "@type": "Site",
20
25
  "country": {"@type": "Term", "termType": "region"}
@@ -22,8 +27,9 @@ REQUIREMENTS = {
22
27
  }
23
28
  }
24
29
  LOOKUPS = {
25
- "region-animalProduct-animalProductGroupingFAO-productionQuantity": "production quantity",
26
- "region-animalProduct-animalProductGroupingFAO-head": "number of heads"
30
+ "liveAnimal": ["primaryMeatProductFaoProductionTermId"],
31
+ "animalProduct": ["animalProductGroupingFAOEquivalent", "animalProductGroupingFAO"],
32
+ "region-animalProduct-animalProductGroupingFAO-averageColdCarcassWeight": "use value from above"
27
33
  }
28
34
  RETURNS = {
29
35
  "Product": [{
@@ -34,6 +40,7 @@ RETURNS = {
34
40
  }]
35
41
  }
36
42
  TERM_ID = 'liveweightPerHead'
43
+ LOOKUP_WEIGHT = 'region-animalProduct-animalProductGroupingFAO-averageColdCarcassWeight.csv'
37
44
 
38
45
 
39
46
  def _property(value: float):
@@ -42,47 +49,76 @@ def _property(value: float):
42
49
  return prop
43
50
 
44
51
 
45
- def _should_run_product(cycle: dict, year: int, country: str):
46
- def exec(product: dict):
47
- product_id = product.get('term', {}).get('@id')
48
- value = product_equivalent_value(product, year, country)
49
- should_run = all([value])
50
- logShouldRun(cycle, MODEL, product_id, should_run, property=TERM_ID)
51
- return should_run, product, value
52
- return exec
52
+ def _product_value(product: dict, year: int, country_id: str):
53
+ product_id = product.get('term', {}).get('@id')
54
+ groupingFAO = get_animalProduct_lookup_value(MODEL, product_id, FAO_LOOKUP_COLUMN)
53
55
 
56
+ lookup = download_lookup(LOOKUP_WEIGHT)
57
+ data = get_table_value(lookup, 'termid', country_id, column_name(groupingFAO))
58
+ debugMissingLookup(LOOKUP_WEIGHT, 'termid', country_id, groupingFAO, data, model=MODEL, term=TERM_ID)
59
+ average_carcass_weight = safe_parse_float(extract_grouped_data_closest_date(data, year), None)
60
+ # average_carcass_weight is in hg, divide by 10 to go back to kg
61
+ kg_carcass_weight = average_carcass_weight / 10 if average_carcass_weight else None
54
62
 
55
- def _run(products: list):
56
- def run_product(values: tuple):
57
- product, value = values
58
- prop = _property(value)
59
- return {**product, 'properties': product.get('properties', []) + [prop]} if prop else None
63
+ kg_liveweight = convert_product_to_unit({
64
+ **product,
65
+ 'value': [kg_carcass_weight]
66
+ }, Units.KG_LIVEWEIGHT) if kg_carcass_weight else None
60
67
 
61
- return non_empty_list(map(run_product, products))
68
+ return kg_liveweight, groupingFAO
62
69
 
63
70
 
64
- def _should_run(cycle: dict):
65
- products = filter_list_term_type(cycle.get('products', []), TermTermType.ANIMALPRODUCT)
66
- products = _filter_list_term_unit(products, Units.KG_LIVEWEIGHT)
67
- products = list(filter(node_has_no_property(TERM_ID), products))
68
- has_kg_liveweight_products = len(products) > 0
71
+ def _should_run_liveAnimal(product: dict):
72
+ return product.get('term', {}).get('termType') == TermTermType.LIVEANIMAL.value
69
73
 
70
- end_date = safe_parse_date(cycle.get('endDate'))
71
- year = end_date.year if end_date else None
72
- country = cycle.get('site', {}).get('country', {}).get('@id')
73
74
 
74
- logRequirements(cycle, model=MODEL, term=TERM_ID,
75
- has_kg_liveweight_products=has_kg_liveweight_products,
75
+ def _run_liveAnimal(cycle: dict, product: dict, year: int, country_id: str):
76
+ # find the animalProduct to get the average carcass weight
77
+ product_id = product.get('term', {}).get('@id')
78
+ animal_product_id = get_liveAnimal_to_animalProduct_id(product_id, LOOKUPS['liveAnimal'][0], term=TERM_ID)
79
+
80
+ animal_product_term = download_hestia(animal_product_id)
81
+ kg_liveweight, groupingFAO = _product_value({**product, 'term': animal_product_term}, year, country_id)
82
+
83
+ logRequirements(cycle, model=MODEL, term=product_id, property=TERM_ID,
84
+ animal_product_id=animal_product_id,
85
+ country_id=country_id,
76
86
  year=year,
77
- country=country)
87
+ kg_liveweight=kg_liveweight,
88
+ groupingFAO=groupingFAO)
78
89
 
79
- should_run = all([has_kg_liveweight_products, year, country])
80
- logShouldRun(cycle, MODEL, TERM_ID, should_run)
81
- return should_run, products, year, country
90
+ should_run = all([kg_liveweight])
91
+ logShouldRun(cycle, MODEL, product_id, should_run, property=TERM_ID)
92
+
93
+ return {**product, 'properties': product.get('properties', []) + [_property(kg_liveweight)]} if should_run else None
94
+
95
+
96
+ def _run_animalProduct(cycle: dict, product: dict, year: int, country_id: str):
97
+ product_id = product.get('term', {}).get('@id')
98
+ kg_liveweight, groupingFAO = _product_value(product, year, country_id)
99
+
100
+ logRequirements(cycle, model=MODEL, term=product_id, property=TERM_ID,
101
+ country_id=country_id,
102
+ year=year,
103
+ kg_liveweight=kg_liveweight,
104
+ groupingFAO=groupingFAO)
105
+
106
+ should_run = all([kg_liveweight])
107
+ logShouldRun(cycle, MODEL, product_id, should_run, property=TERM_ID)
108
+
109
+ return {**product, 'properties': product.get('properties', []) + [_property(kg_liveweight)]} if should_run else None
82
110
 
83
111
 
84
112
  def run(cycle: dict):
85
- should_run, products, year, country = _should_run(cycle)
86
- products = list(map(_should_run_product(cycle, year, country), products)) if should_run else []
87
- products = [(product, value) for (should_run, product, value) in products if should_run]
88
- return _run(products) if should_run else []
113
+ country_id = cycle.get('site', {}).get('country', {}).get('@id')
114
+ end_date = safe_parse_date(cycle.get('endDate'))
115
+ year = end_date.year if end_date else None
116
+
117
+ products = filter_list_term_type(cycle.get('products', []), [TermTermType.ANIMALPRODUCT, TermTermType.LIVEANIMAL])
118
+ products = list(filter(node_has_no_property(TERM_ID), products))
119
+
120
+ return non_empty_list([
121
+ (
122
+ (_run_liveAnimal if _should_run_liveAnimal(p) else _run_animalProduct)(cycle, p, year, country_id)
123
+ ) for p in products
124
+ ]) if country_id else []
@@ -12,10 +12,9 @@ from hestia_earth.models.log import debugMissingLookup, debugValues, logRequirem
12
12
  from hestia_earth.models.utils.constant import Units
13
13
  from hestia_earth.models.utils.currency import DEFAULT_CURRENCY
14
14
  from hestia_earth.models.utils.crop import FAOSTAT_PRODUCTION_LOOKUP_COLUMN, get_crop_grouping_faostat_production
15
- from hestia_earth.models.utils.animalProduct import (
16
- FAO_LOOKUP_COLUMN, get_animalProduct_grouping_fao, get_animalProduct_lookup_value
17
- )
15
+ from hestia_earth.models.utils.animalProduct import FAO_LOOKUP_COLUMN, get_animalProduct_grouping_fao
18
16
  from hestia_earth.models.utils.product import convert_product_to_unit
17
+ from ..utils import get_liveAnimal_to_animalProduct_id
19
18
  from .. import MODEL
20
19
 
21
20
  REQUIREMENTS = {
@@ -39,8 +38,8 @@ LOOKUPS = {
39
38
  "@doc": "Depending on the primary product [termType](https://hestia.earth/schema/Product#term)",
40
39
  "crop": "cropGroupingFaostatProduction",
41
40
  "region-crop-cropGroupingFaostatProduction-price": "use value from above",
42
- "liveAnimal": "primaryMeatProductFAO",
43
- "animalProduct": ["animalProductGroupingFAOEquivalent", "animalProductGroupingFAO", "liveAnimal"],
41
+ "liveAnimal": ["primaryMeatProductFaoPriceTermId"],
42
+ "animalProduct": ["animalProductGroupingFAOEquivalent", "animalProductGroupingFAO"],
44
43
  "region-animalProduct-animalProductGroupingFAO-price": "use value from above",
45
44
  "region-animalProduct-animalProductGroupingFAO-averageColdCarcassWeight": "use value from above"
46
45
  }
@@ -53,7 +52,6 @@ LOOKUP_GROUPING = {
53
52
  TermTermType.CROP.value: get_crop_grouping_faostat_production,
54
53
  TermTermType.ANIMALPRODUCT.value: get_animalProduct_grouping_fao
55
54
  }
56
- LOOKUP_WEIGHT = 'region-animalProduct-animalProductGroupingFAO-averageColdCarcassWeight.csv'
57
55
 
58
56
 
59
57
  def _term_grouping(term: dict): return LOOKUP_GROUPING.get(term.get('termType'), lambda *_: None)(MODEL, term)
@@ -65,8 +63,7 @@ def _lookup_data(
65
63
  lookup_name = lookup_name or LOOKUP_NAME.get(term_type)
66
64
  lookup = download_lookup(lookup_name)
67
65
  data = get_table_value(lookup, 'termid', country_id, column_name(grouping))
68
- debugMissingLookup(lookup_name, 'termid', country_id, grouping, data,
69
- model=MODEL, term=term_id, key=MODEL_KEY)
66
+ debugMissingLookup(lookup_name, 'termid', country_id, grouping, data, model=MODEL, term=term_id, key=MODEL_KEY)
70
67
  price = extract_grouped_data(data, str(year)) or extract_grouped_data(data, 'Average_price_per_tonne')
71
68
  return safe_parse_float(price, None)
72
69
 
@@ -76,58 +73,46 @@ def _product(product: dict, value: float):
76
73
  return product | {'currency': DEFAULT_CURRENCY, MODEL_KEY: round(value, 4)}
77
74
 
78
75
 
79
- def _get_animalProductId(term_id: str):
80
- lookup_name = 'liveAnimal.csv'
81
- lookup = download_lookup(lookup_name)
82
- value = get_table_value(lookup, 'termid', term_id, column_name(LOOKUPS.get('liveAnimal')))
83
- debugMissingLookup(lookup_name, 'termid', term_id, LOOKUPS.get('liveAnimal'), value,
84
- model=MODEL, term=term_id, key=MODEL_KEY)
85
- return value
86
-
87
-
88
76
  def _get_liveAnimal_lookup_values(cycle: dict, product: dict, country_id: str, year: int = None):
89
77
  term_id = product.get('term', {}).get('@id')
90
- animal_product = _get_animalProductId(term_id)
91
- groupingFAO = get_animalProduct_lookup_value(MODEL, animal_product, FAO_LOOKUP_COLUMN) if animal_product else None
78
+ animal_product = get_liveAnimal_to_animalProduct_id(term_id, LOOKUPS['liveAnimal'][0], key=MODEL_KEY)
79
+ groupingFAO = _term_grouping({'termType': TermTermType.ANIMALPRODUCT.value, '@id': animal_product})
92
80
 
93
81
  # one live animal can be linked to many animal product, hence go one by one until we have a match
94
82
  if groupingFAO:
95
- average_carcass_weight = _lookup_data(animal_product, groupingFAO, country_id, year, lookup_name=LOOKUP_WEIGHT)
96
- price = _lookup_data(term_id, groupingFAO, country_id, year, term_type=TermTermType.ANIMALPRODUCT.value)
83
+ price_per_ton_liveweight = _lookup_data(
84
+ term_id, groupingFAO, country_id, year, term_type=TermTermType.ANIMALPRODUCT.value
85
+ )
97
86
  debugValues(cycle, model=MODEL, term=term_id, key=MODEL_KEY, by='liveAnimal',
98
87
  animal_product=animal_product,
99
- groupingFAO=f"'{groupingFAO}'",
100
- average_carcass_weight_hg=average_carcass_weight,
101
- price_per_ton=price)
102
- if price and average_carcass_weight:
88
+ price_per_ton_liveweight=price_per_ton_liveweight,
89
+ groupingFAO=f"'{groupingFAO}'")
90
+ if price_per_ton_liveweight:
103
91
  # price is per 1000kg, divide by 1000 to go back to USD/kg
104
- # average_carcass_weight is in hg, divide by 10 to go back to kg
105
- return (animal_product, price / 1000, average_carcass_weight / 10)
106
- return (None, None, None)
92
+ return (animal_product, price_per_ton_liveweight / 1000)
93
+ return (None, None)
107
94
 
108
95
 
109
96
  def _run_by_liveAnimal(cycle: dict, product: dict, country_id: str, year: int = None):
110
97
  term_id = product.get('term', {}).get('@id')
111
- animal_product, price, carcass_weight = _get_liveAnimal_lookup_values(cycle, product, country_id, year)
98
+ animal_product_id, price_per_kg_liveweight = _get_liveAnimal_lookup_values(cycle, product, country_id, year)
112
99
 
113
- animal_product = download_hestia(animal_product)
114
- price_per_carcass_weight = convert_product_to_unit({
100
+ animal_product = download_hestia(animal_product_id)
101
+ price_per_head = convert_product_to_unit({
102
+ **product,
115
103
  'term': animal_product,
116
- 'value': [price]
117
- }, Units.KG_COLD_CARCASS_WEIGHT) if price else None
118
- should_run = all([price_per_carcass_weight, carcass_weight])
119
- value = price_per_carcass_weight * carcass_weight if should_run else None
104
+ 'value': [price_per_kg_liveweight]
105
+ }, Units.HEAD) if price_per_kg_liveweight else None
120
106
 
121
107
  logRequirements(cycle, model=MODEL, term=term_id, key=MODEL_KEY, by='liveAnimal',
122
- price_from_lookup=price,
123
- carcass_weight_kg=carcass_weight,
124
- price_per_carcass_weight=price_per_carcass_weight,
125
108
  year=year,
126
- price=value)
109
+ price_per_kg_liveweight=price_per_kg_liveweight,
110
+ price_per_head=price_per_head)
127
111
 
112
+ should_run = all([animal_product, price_per_head])
128
113
  logShouldRun(cycle, MODEL, term_id, should_run, key=MODEL_KEY, by='liveAnimal')
129
114
 
130
- return _product(product, value) if value is not None else None
115
+ return _product(product, price_per_head) if price_per_head is not None else None
131
116
 
132
117
 
133
118
  def _should_run_liveAnimal(product: dict):
@@ -151,9 +136,9 @@ def _run_by_country(cycle: dict, product: dict, country_id: str, year: int = Non
151
136
  logRequirements(cycle, model=MODEL, term=term_id, key=MODEL_KEY, by='country',
152
137
  has_yield=has_yield,
153
138
  not_already_set=not_already_set,
154
- groupingFAO=f"'{grouping}'",
155
139
  year=year,
156
- price_per_ton=value)
140
+ price_per_ton=value,
141
+ groupingFAO=f"'{grouping}'")
157
142
 
158
143
  logShouldRun(cycle, MODEL, term_id, should_run, key=MODEL_KEY, by='country')
159
144
 
@@ -165,27 +150,17 @@ def _should_run_product(product: dict):
165
150
  return product.get(MODEL_KEY) is None
166
151
 
167
152
 
168
- def _should_run(cycle: dict):
169
- country_id = cycle.get('site', {}).get('country', {}).get('@id')
170
-
171
- logRequirements(cycle, model=MODEL, key=MODEL_KEY,
172
- country_id=country_id)
173
-
174
- should_run = all([country_id])
175
- logShouldRun(cycle, MODEL, None, should_run, key=MODEL_KEY)
176
- return should_run, country_id
177
-
178
-
179
153
  def run(cycle: dict):
180
- should_run_by_country, country_id = _should_run(cycle)
154
+ country_id = cycle.get('site', {}).get('country', {}).get('@id')
181
155
  end_date = safe_parse_date(cycle.get('endDate'))
182
156
  year = end_date.year if end_date else None
157
+
183
158
  products = list(filter(_should_run_product, cycle.get('products', [])))
184
159
  return non_empty_list([
185
160
  (
186
161
  (
187
162
  (_run_by_liveAnimal(cycle, p, country_id, year) if _should_run_liveAnimal(p) else None)
188
163
  or _run_by_country(cycle, p, country_id, year)
189
- ) if should_run_by_country else None
164
+ ) if country_id else None
190
165
  ) for p in products
191
166
  ])
@@ -3,7 +3,7 @@ from hestia_earth.utils.api import download_hestia
3
3
  from hestia_earth.utils.lookup import download_lookup, get_table_value, column_name, extract_grouped_data_closest_date
4
4
  from hestia_earth.utils.tools import safe_parse_float
5
5
 
6
- from hestia_earth.models.log import logger
6
+ from hestia_earth.models.log import logger, debugMissingLookup
7
7
  from hestia_earth.models.utils.animalProduct import (
8
8
  FAO_LOOKUP_COLUMN, FAO_EQUIVALENT_LOOKUP_COLUMN, get_animalProduct_lookup_value
9
9
  )
@@ -13,9 +13,17 @@ from . import MODEL
13
13
  LOOKUP_PREFIX = f"{TermTermType.REGION.value}-{TermTermType.ANIMALPRODUCT.value}-{FAO_LOOKUP_COLUMN}"
14
14
 
15
15
 
16
+ def get_liveAnimal_to_animalProduct_id(product_term_id: str, column: str, **log_args):
17
+ lookup_name = 'liveAnimal.csv'
18
+ lookup = download_lookup(lookup_name)
19
+ value = get_table_value(lookup, 'termid', product_term_id, column_name(column))
20
+ debugMissingLookup(lookup_name, 'termid', product_term_id, column, value, model=MODEL, **log_args)
21
+ return value
22
+
23
+
16
24
  def product_equivalent_value(product: dict, year: int, country: str):
17
25
  term_id = product.get('term', {}).get('@id')
18
- fao_product_id = get_animalProduct_lookup_value(MODEL, term_id, FAO_EQUIVALENT_LOOKUP_COLUMN)
26
+ fao_product_id = get_animalProduct_lookup_value(MODEL, term_id, FAO_EQUIVALENT_LOOKUP_COLUMN) or term_id
19
27
  grouping = get_animalProduct_lookup_value(MODEL, fao_product_id, FAO_LOOKUP_COLUMN)
20
28
 
21
29
  if not grouping or not fao_product_id:
@@ -47,9 +47,9 @@ def _measurement(value: float):
47
47
 
48
48
 
49
49
  def _download(site: dict):
50
- scale = 10
50
+ factor = 0.1
51
51
  value = download(TERM_ID, site, EE_PARAMS)
52
- return value / scale if value else None
52
+ return value * factor if value else None
53
53
 
54
54
 
55
55
  def _run(site: dict):