Skip to content

Commit

Permalink
feat: set max percent of sugar and salt ingredients based on nutritio…
Browse files Browse the repository at this point in the history
…n facts (#9276)
  • Loading branch information
stephanegigandet authored Nov 16, 2023
1 parent 040f6ca commit 80bcc29
Show file tree
Hide file tree
Showing 15 changed files with 324 additions and 189 deletions.
2 changes: 1 addition & 1 deletion lib/ProductOpener/DataQualityFood.pm
Original file line number Diff line number Diff line change
Expand Up @@ -940,7 +940,7 @@ sub check_nutrition_data ($product_ref) {

# some categories have an expected ingredient - push data quality error if ingredient differs from expected ingredient
# note: we currently support only 1 expected ingredient
my ($expected_ingredients, $category_id)
my ($expected_ingredients, $category_id2)
= get_inherited_property_from_categories_tags($product_ref, "expected_ingredients:en");

if ((defined $expected_ingredients)) {
Expand Down
55 changes: 55 additions & 0 deletions lib/ProductOpener/Ingredients.pm
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ use JSON::PP;
use Log::Any qw($log);
use List::MoreUtils qw(uniq);
use Test::More;
use Data::DeepAccess qw(deep_get deep_exists);

# MIDDLE DOT with common substitutes (BULLET variants, BULLET OPERATOR and DOT OPERATOR (multiplication))
# U+00B7 "·" (Middle Dot). Is a common character in Catalan. To avoid to break ingredients,
Expand Down Expand Up @@ -887,6 +888,57 @@ sub add_properties_from_specific_ingredients ($product_ref) {
return;
}

=head2 add_percent_max_for_ingredients_from_nutrition_facts ( $product_ref )
Add a percent_max value for salt and sugar ingredients, based on the nutrition facts.
=cut

sub add_percent_max_for_ingredients_from_nutrition_facts ($product_ref) {

# Check if we have values for salt and sugar in the nutrition facts
my @ingredient_max_values = ();
my $sugars_100g = deep_get($product_ref, qw(nutriments sugars_100g));
if (defined $sugars_100g) {
push @ingredient_max_values, {ingredientid => "en:sugar", value => $sugars_100g};
}
my $salt_100g = deep_get($product_ref, qw(nutriments salt_100g));
if (defined $salt_100g) {
push @ingredient_max_values, {ingredientid => "en:salt", value => $salt_100g};
}

if (scalar @ingredient_max_values) {

# Traverse the ingredients tree, depth first

my @ingredients = @{$product_ref->{ingredients}};

while (@ingredients) {

# Remove and process the first ingredient
my $ingredient_ref = shift @ingredients;
my $ingredientid = $ingredient_ref->{id};

# Add sub-ingredients at the beginning of the ingredients array
if (defined $ingredient_ref->{ingredients}) {

unshift @ingredients, @{$ingredient_ref->{ingredients}};
}

foreach my $ingredient_max_value_ref (@ingredient_max_values) {
my $value = $ingredient_max_value_ref->{value};
if (is_a("ingredients", $ingredient_ref->{id}, $ingredient_max_value_ref->{ingredientid})) {
if (not defined $ingredient_ref->{percent_max}) {
$ingredient_ref->{percent_max} = $value;
}
}

}
}
}
return;
}

=head2 add_specific_ingredients_from_labels ( product_ref )
Check if the product has labels that indicate properties (e.g. origins) for specific ingredients.
Expand Down Expand Up @@ -2913,6 +2965,9 @@ reference to a hash of product fields that have been created or updated

sub estimate_ingredients_percent_service ($product_ref, $updated_product_fields_ref) {

# Add a percent_max value for salt and sugar ingredients, based on the nutrition facts.
add_percent_max_for_ingredients_from_nutrition_facts($product_ref);

if (compute_ingredients_percent_min_max_values(100, 100, $product_ref->{ingredients}) < 0) {

# The computation yielded seemingly impossible values, delete the values
Expand Down
70 changes: 35 additions & 35 deletions tests/unit/expected_test_results/attributes/en-attributes.json
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@
"grade" : "a",
"icon_url" : "https://server_domain/images/attributes/forest-footprint-a.svg",
"id" : "forest_footprint",
"match" : 99.9292929292929,
"match" : 99.9222222222222,
"name" : "Forest footprint",
"status" : "known",
"title" : "Very small forest footprint"
Expand Down Expand Up @@ -412,12 +412,12 @@
"percent" : 54.5454545454545
},
{
"origin" : "en:paraguay",
"percent" : 22.7272727272727
"origin" : "en:unknown",
"percent" : 32.9545454545455
},
{
"origin" : "en:unknown",
"percent" : 22.7272727272727
"origin" : "en:paraguay",
"percent" : 12.5
}
],
"epi_score" : 52.9090909090909,
Expand Down Expand Up @@ -818,7 +818,7 @@
],
"food_groups_tags" : [],
"forest_footprint_data" : {
"footprint_per_kg" : 0.00176767676767677,
"footprint_per_kg" : 0.00194444444444444,
"grade" : "a",
"ingredients" : [
{
Expand All @@ -828,10 +828,10 @@
"en:organic"
]
],
"footprint_per_kg" : 0.00176767676767677,
"footprint_per_kg" : 0.00194444444444444,
"matching_tag_id" : "en:egg",
"percent" : 11.3636363636364,
"percent_estimate" : 11.3636363636364,
"percent" : 12.5,
"percent_estimate" : 12.5,
"processing_factor" : 1,
"tag_id" : "en:egg",
"tag_type" : "ingredients",
Expand All @@ -858,8 +858,8 @@
{
"id" : "en:sugar",
"origins" : "en:paraguay",
"percent_estimate" : 22.7272727272727,
"percent_max" : 50,
"percent_estimate" : 12.5,
"percent_max" : 25,
"percent_min" : 0,
"text" : "sugar",
"vegan" : "yes",
Expand All @@ -868,8 +868,8 @@
{
"ciqual_food_code" : "22000",
"id" : "en:egg",
"percent_estimate" : 11.3636363636364,
"percent_max" : 33.3333333333333,
"percent_estimate" : 12.5,
"percent_max" : 25,
"percent_min" : 0,
"text" : "eggs",
"vegan" : "no",
Expand All @@ -878,7 +878,7 @@
{
"ciqual_food_code" : "13014",
"id" : "en:strawberry",
"percent_estimate" : 5.68181818181818,
"percent_estimate" : 10.2272727272727,
"percent_max" : 25,
"percent_min" : 0,
"text" : "strawberries",
Expand All @@ -888,7 +888,7 @@
{
"ciqual_food_code" : "31077",
"id" : "en:high-fructose-corn-syrup",
"percent_estimate" : 2.84090909090909,
"percent_estimate" : 5.11363636363636,
"percent_max" : 20,
"percent_min" : 0,
"text" : "high fructose corn syrup",
Expand All @@ -898,7 +898,7 @@
{
"from_palm_oil" : "no",
"id" : "en:rapeseed-oil",
"percent_estimate" : 1.42045454545455,
"percent_estimate" : 2.55681818181818,
"percent_max" : 16.6666666666667,
"percent_min" : 0,
"text" : "rapeseed oil",
Expand All @@ -908,7 +908,7 @@
{
"ciqual_food_code" : "15027",
"id" : "en:macadamia-nut",
"percent_estimate" : 0.710227272727273,
"percent_estimate" : 1.27840909090909,
"percent_max" : 14.2857142857143,
"percent_min" : 0,
"text" : "macadamia nuts",
Expand All @@ -917,7 +917,7 @@
},
{
"id" : "en:milk-proteins",
"percent_estimate" : 0.35511363636364,
"percent_estimate" : 0.639204545454547,
"percent_max" : 12.5,
"percent_min" : 0,
"text" : "milk proteins",
Expand All @@ -927,26 +927,26 @@
{
"ciqual_food_code" : "11058",
"id" : "en:salt",
"percent_estimate" : 0.17755681818182,
"percent_max" : 11.1111111111111,
"percent_estimate" : 0.125,
"percent_max" : 0.25,
"percent_min" : 0,
"text" : "salt",
"vegan" : "yes",
"vegetarian" : "yes"
},
{
"id" : "en:e102",
"percent_estimate" : 0.0887784090909065,
"percent_max" : 10,
"percent_estimate" : 0.125,
"percent_max" : 0.25,
"percent_min" : 0,
"text" : "e102",
"vegan" : "yes",
"vegetarian" : "yes"
},
{
"id" : "en:e120",
"percent_estimate" : 0.0887784090909065,
"percent_max" : 9.09090909090909,
"percent_estimate" : 0.389204545454547,
"percent_max" : 0.25,
"percent_min" : 0,
"text" : "e120",
"vegan" : "no",
Expand Down Expand Up @@ -1163,10 +1163,10 @@
"energy_100g" : 800,
"fat_100g" : 12,
"fiber_100g" : 3,
"fruits-vegetables-legumes-estimate-from-ingredients_100g" : 5.68181818181818,
"fruits-vegetables-legumes-estimate-from-ingredients_serving" : 5.68181818181818,
"fruits-vegetables-nuts-estimate-from-ingredients_100g" : 7.8125,
"fruits-vegetables-nuts-estimate-from-ingredients_serving" : 7.8125,
"fruits-vegetables-legumes-estimate-from-ingredients_100g" : 10.2272727272727,
"fruits-vegetables-legumes-estimate-from-ingredients_serving" : 10.2272727272727,
"fruits-vegetables-nuts-estimate-from-ingredients_100g" : 14.0625,
"fruits-vegetables-nuts-estimate-from-ingredients_serving" : 14.0625,
"nova-group" : 4,
"nova-group_100g" : 4,
"nova-group_serving" : 4,
Expand All @@ -1188,9 +1188,9 @@
"fiber" : 3,
"fiber_points" : 3,
"fiber_value" : 3,
"fruits_vegetables_nuts_colza_walnut_olive_oils" : 7.8125,
"fruits_vegetables_nuts_colza_walnut_olive_oils" : 14.0625,
"fruits_vegetables_nuts_colza_walnut_olive_oils_points" : 0,
"fruits_vegetables_nuts_colza_walnut_olive_oils_value" : 7.8,
"fruits_vegetables_nuts_colza_walnut_olive_oils_value" : 14.1,
"is_beverage" : 0,
"is_cheese" : 0,
"is_fat" : 0,
Expand Down Expand Up @@ -1225,7 +1225,7 @@
"energy_points" : 2,
"fiber" : 3,
"fiber_points" : 0,
"fruits_vegetables_legumes" : 5.68181818181818,
"fruits_vegetables_legumes" : 10.2272727272727,
"fruits_vegetables_legumes_points" : 0,
"is_beverage" : 0,
"is_cheese" : 0,
Expand Down Expand Up @@ -1274,9 +1274,9 @@
"fiber" : 3,
"fiber_points" : 3,
"fiber_value" : 3,
"fruits_vegetables_nuts_colza_walnut_olive_oils" : 7.8125,
"fruits_vegetables_nuts_colza_walnut_olive_oils" : 14.0625,
"fruits_vegetables_nuts_colza_walnut_olive_oils_points" : 0,
"fruits_vegetables_nuts_colza_walnut_olive_oils_value" : 7.8,
"fruits_vegetables_nuts_colza_walnut_olive_oils_value" : 14.1,
"grade" : "c",
"is_beverage" : 0,
"is_cheese" : 0,
Expand Down Expand Up @@ -1315,9 +1315,9 @@
"nutrition_score_beverage" : 0,
"nutrition_score_debug" : "",
"nutrition_score_warning_fruits_vegetables_legumes_estimate_from_ingredients" : 1,
"nutrition_score_warning_fruits_vegetables_legumes_estimate_from_ingredients_value" : 5.68181818181818,
"nutrition_score_warning_fruits_vegetables_legumes_estimate_from_ingredients_value" : 10.2272727272727,
"nutrition_score_warning_fruits_vegetables_nuts_estimate_from_ingredients" : 1,
"nutrition_score_warning_fruits_vegetables_nuts_estimate_from_ingredients_value" : 7.8125,
"nutrition_score_warning_fruits_vegetables_nuts_estimate_from_ingredients_value" : 14.0625,
"other_nutritional_substances_tags" : [],
"packaging_materials_tags" : [
"en:cardboard"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[
{
"id" : "en:water",
"percent_estimate" : 92.5,
"percent_max" : 100,
"percent_min" : 85,
"text" : "water"
},
{
"id" : "en:sugar",
"percent_estimate" : 3.75,
"percent_max" : 10,
"percent_min" : 0,
"text" : "sugar"
},
{
"id" : "en:salt",
"percent_estimate" : 3.75,
"percent_max" : 5,
"percent_min" : 0,
"text" : "salt"
}
]
Loading

0 comments on commit 80bcc29

Please sign in to comment.