hestia-earth-models 0.59.5__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.
- hestia_earth/models/cycle/animal/milkYield.py +86 -0
- hestia_earth/models/cycle/endDate.py +50 -0
- hestia_earth/models/cycle/inorganicFertiliser.py +3 -2
- hestia_earth/models/cycle/milkYield.py +8 -3
- hestia_earth/models/cycle/pre_checks/__init__.py +1 -2
- hestia_earth/models/cycle/startDate.py +42 -0
- hestia_earth/models/faostat2018/liveweightPerHead.py +77 -41
- hestia_earth/models/faostat2018/product/price.py +30 -55
- hestia_earth/models/faostat2018/utils.py +10 -2
- hestia_earth/models/haversineFormula/transport/distance.py +6 -3
- hestia_earth/models/ipcc2006/n2OToAirInorganicFertiliserIndirect.py +1 -1
- hestia_earth/models/ipcc2019/organicCarbonPerHa.py +19 -4
- hestia_earth/models/ipcc2019/pastureGrass.py +2 -1
- hestia_earth/models/linkedImpactAssessment/__init__.py +3 -3
- hestia_earth/models/mocking/search-results.json +244 -232
- hestia_earth/models/schmidt2007/h2SToAirWasteTreatment.py +58 -0
- hestia_earth/models/site/management.py +3 -1
- hestia_earth/models/utils/__init__.py +4 -1
- hestia_earth/models/utils/animalProduct.py +6 -4
- hestia_earth/models/utils/blank_node.py +3 -2
- hestia_earth/models/utils/product.py +9 -1
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.59.5.dist-info → hestia_earth_models-0.59.6.dist-info}/METADATA +1 -1
- {hestia_earth_models-0.59.5.dist-info → hestia_earth_models-0.59.6.dist-info}/RECORD +35 -29
- tests/models/cycle/animal/test_milkYield.py +43 -0
- tests/models/cycle/test_endDate.py +24 -0
- tests/models/cycle/test_startDate.py +22 -0
- tests/models/faostat2018/product/test_price.py +25 -45
- tests/models/faostat2018/test_liveweightPerHead.py +106 -42
- tests/models/ipcc2019/test_organicCarbonPerHa.py +8 -0
- tests/models/schmidt2007/test_h2SToAirWasteTreatment.py +45 -0
- tests/models/utils/test_blank_node.py +71 -3
- hestia_earth/models/cycle/pre_checks/startDate.py +0 -52
- tests/models/cycle/pre_checks/test_startDate.py +0 -44
- {hestia_earth_models-0.59.5.dist-info → hestia_earth_models-0.59.6.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.59.5.dist-info → hestia_earth_models-0.59.6.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.59.5.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
|
-
"
|
|
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):
|
|
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):
|
|
@@ -48,7 +48,7 @@ RETURNS = {
|
|
|
48
48
|
}]
|
|
49
49
|
}
|
|
50
50
|
LOOKUPS = {
|
|
51
|
-
"animalProduct": ["liveAnimalTermId", "
|
|
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 =
|
|
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 =
|
|
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,
|
|
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
|
|
@@ -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
|
-
|
|
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
|
-
"
|
|
26
|
-
"
|
|
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
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
|
68
|
+
return kg_liveweight, groupingFAO
|
|
62
69
|
|
|
63
70
|
|
|
64
|
-
def
|
|
65
|
-
|
|
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
|
-
|
|
75
|
-
|
|
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
|
-
|
|
87
|
+
kg_liveweight=kg_liveweight,
|
|
88
|
+
groupingFAO=groupingFAO)
|
|
78
89
|
|
|
79
|
-
should_run = all([
|
|
80
|
-
logShouldRun(cycle, MODEL,
|
|
81
|
-
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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": "
|
|
43
|
-
"animalProduct": ["animalProductGroupingFAOEquivalent", "animalProductGroupingFAO"
|
|
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 =
|
|
91
|
-
groupingFAO =
|
|
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
|
-
|
|
96
|
-
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
105
|
-
|
|
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
|
-
|
|
98
|
+
animal_product_id, price_per_kg_liveweight = _get_liveAnimal_lookup_values(cycle, product, country_id, year)
|
|
112
99
|
|
|
113
|
-
animal_product = download_hestia(
|
|
114
|
-
|
|
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': [
|
|
117
|
-
}, Units.
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
|
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:
|
|
@@ -27,8 +27,11 @@ REQUIREMENTS = {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
RETURNS = {
|
|
30
|
-
"
|
|
31
|
-
"
|
|
30
|
+
"Input": [{
|
|
31
|
+
"transport": [{
|
|
32
|
+
"@type": "Transport",
|
|
33
|
+
"distance": ""
|
|
34
|
+
}]
|
|
32
35
|
}]
|
|
33
36
|
}
|
|
34
37
|
MODEL_KEY = 'distance'
|
|
@@ -98,7 +101,7 @@ def _should_run(cycle: dict):
|
|
|
98
101
|
# can only run if the site country has centroid coordinates
|
|
99
102
|
logRequirements(cycle, model=MODEL, term=None, key=MODEL_KEY,
|
|
100
103
|
latitude=country.get('latitude'),
|
|
101
|
-
longitude=country.get('
|
|
104
|
+
longitude=country.get('longitude'),
|
|
102
105
|
has_inputs_transport=len(inputs) > 0)
|
|
103
106
|
should_run = all([country.get('latitude'), country.get('latitude'), len(inputs) > 0])
|
|
104
107
|
logShouldRun(cycle, MODEL, None, should_run, key=MODEL_KEY)
|