Skip to content

Commit

Permalink
fix: Nutri-Score A to B threshold and olive oils (#9190)
Browse files Browse the repository at this point in the history
* fix A/B threshold for non fat food

* fixes for olive oil and avocado oil

* assume olive oil category has for ingredient olive oil
  • Loading branch information
stephanegigandet authored Oct 25, 2023
1 parent 9a0bfba commit 4f04c19
Show file tree
Hide file tree
Showing 24 changed files with 2,311 additions and 85 deletions.
7 changes: 6 additions & 1 deletion lib/ProductOpener/Food.pm
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ BEGIN {
&is_beverage_for_nutrition_score_2021
&is_beverage_for_nutrition_score_2023
&is_fat_oil_nuts_seeds_for_nutrition_score
&is_water_for_nutrition_score
&is_cheese_for_nutrition_score
&is_fat_for_nutrition_score
Expand Down Expand Up @@ -1288,6 +1289,10 @@ my @fruits_vegetables_legumes_by_category_sorted = (
["en:canned-fruits", 90],
["en:frozen-fruits", 90],
["en:jams", 50],
# for products in the fat/oil/nuts/seeds category
["en:avocado-oils", 100],
["en:olive-oils", 100],

);

=head2 compute_nutriscore_2023_fruits_vegetables_legumes($product_ref, $prepared)
Expand Down Expand Up @@ -1516,7 +1521,7 @@ sub compute_nutriscore_data ($product_ref, $prepared, $nutriments_field, $versio

if ($is_fat_oil_nuts_seeds) {
# Add the fat and saturated fat / fat ratio
$nutriscore_data_ref->{fat} = $nutriments_ref->{"fat" . $prepared};
$nutriscore_data_ref->{fat} = $nutriments_ref->{"fat" . $prepared . "_100g"};
$nutriscore_data_ref->{saturated_fat_ratio} = saturated_fat_ratio($nutriments_ref, $prepared);
# Compute the energy from saturates
if (defined $nutriscore_data_ref->{saturated_fat}) {
Expand Down
36 changes: 31 additions & 5 deletions lib/ProductOpener/Ingredients.pm
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ use ProductOpener::URL qw/:all/;
use ProductOpener::Images qw/:all/;
use ProductOpener::Lang qw/:all/;
use ProductOpener::Units qw/:all/;
use ProductOpener::Food qw/is_fat_oil_nuts_seeds_for_nutrition_score/;

use Encode;
use Clone qw(clone);
Expand Down Expand Up @@ -6860,7 +6861,7 @@ sub estimate_nutriscore_2021_fruits_vegetables_nuts_percent_from_ingredients ($p

}

=head2 is_fruits_vegetables_legumes ( $ingredient_id, $processing = undef )
=head2 is_fruits_vegetables_legumes ( $ingredient_id, $processing = undef)
Determine if an ingredient should be counted as "fruits, vegetables, legumes"
in Nutriscore 2023 algorithm.
Expand Down Expand Up @@ -6898,6 +6899,9 @@ o 9.60 (Fruit mixtures).
Pulses groups
o 7.10 (Pulses).
Additionally, in the fats and oils category specifically, oils derived from ingredients in the list qualify
for the component (e.g. olive and avocado).
--
NUTRI-SCORE FREQUENTLY ASKED QUESTIONS - UPDATED 27/09/2022:
Expand Down Expand Up @@ -6960,6 +6964,21 @@ sub is_fruits_vegetables_legumes ($ingredient_id, $processing = undef) {
);
}

sub is_fruits_vegetables_legumes_for_fat_oil_nuts_seed ($ingredient_id, $processing = undef) {

# for fat/oil/nuts/seeds products, oils of fruits and vegetables count for fruits and vegetables
return (
is_fruits_vegetables_legumes($ingredient_id, $processing)
or (
(
($ingredient_id eq "en:olive-oil")
or ($ingredient_id eq "en:avocado-oil")
)
)
or 0
);
}

=head2 estimate_nutriscore_2023_fruits_vegetables_legumes_percent_from_ingredients ( product_ref )
This function analyzes the ingredients to estimate the minimum percentage of
Expand All @@ -6971,10 +6990,17 @@ Results are stored in $product_ref->{nutriments}{"fruits-vegetables-legumes-esti

sub estimate_nutriscore_2023_fruits_vegetables_legumes_percent_from_ingredients ($product_ref) {

return estimate_ingredients_matching_function(
$product_ref,
\&is_fruits_vegetables_legumes,
"fruits-vegetables-legumes-estimate-from-ingredients"
# For fat/oil/nuts/seeds products, oils of fruits and vegetables count for fruits and vegetables
my $matching_function_ref;
if (is_fat_oil_nuts_seeds_for_nutrition_score($product_ref)) {
$matching_function_ref = \&is_fruits_vegetables_legumes_for_fat_oil_nuts_seed;
}
else {
$matching_function_ref = \&is_fruits_vegetables_legumes;
}

return estimate_ingredients_matching_function($product_ref, $matching_function_ref,
"fruits-vegetables-legumes-estimate-from-ingredients",
);
}

Expand Down
31 changes: 26 additions & 5 deletions lib/ProductOpener/Nutriscore.pm
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ of a food product.
is_water => 0,
is_cheese => 0,
is_fat => 1, # for 2021 version
is_fat_nuts_seed => 1, # for 2023 version
is_fat_oil_nuts_seed => 1, # for 2023 version
}
my ($nutriscore_score, $nutriscore_grade) = compute_nutriscore_score_and_grade(
Expand Down Expand Up @@ -351,7 +351,7 @@ sub compute_nutriscore_score_2021 ($nutriscore_data_ref) {
}
}

# Special case for sugar: we need to round to 2 digits if we are closed to a threshold defined with 1 digit (e.g. 4.5)
# Special case for sugar: we need to round to 2 digits if we are close to a threshold defined with 1 digit (e.g. 4.5)
# but if the threshold is defined with 0 digit (e.g. 9) we need to round with 1 digit.
if ( (($nutriscore_data_ref->{"sugars_value"} - int($nutriscore_data_ref->{"sugars_value"})) > 0.9)
or (($nutriscore_data_ref->{"sugars_value"} - int($nutriscore_data_ref->{"sugars_value"})) < 0.1))
Expand Down Expand Up @@ -547,7 +547,8 @@ sub compute_nutriscore_score_and_grade_2023 ($nutriscore_data_ref) {
my $nutrition_grade = compute_nutriscore_grade_2023(
$nutrition_score,
$nutriscore_data_ref->{is_beverage},
$nutriscore_data_ref->{is_water}
$nutriscore_data_ref->{is_water},
$nutriscore_data_ref->{is_fat_oil_nuts_seeds},
);

return ($nutrition_score, $nutrition_grade);
Expand Down Expand Up @@ -791,14 +792,15 @@ sub compute_nutriscore_score_2023 ($nutriscore_data_ref) {
return $score;
}

sub compute_nutriscore_grade_2023 ($nutrition_score, $is_beverage, $is_water) {
sub compute_nutriscore_grade_2023 ($nutrition_score, $is_beverage, $is_water, $is_fat_oil_nuts_seeds) {

my $grade = "";

if (not defined $nutrition_score) {
return '';
}

# Beverages
if ($is_beverage) {

if ($is_water) {
Expand All @@ -817,9 +819,28 @@ sub compute_nutriscore_grade_2023 ($nutrition_score, $is_beverage, $is_water) {
$grade = 'e';
}
}
# Fats, oils, nuts and seeds
elsif ($is_fat_oil_nuts_seeds) {
if ($nutrition_score <= -6) {
$grade = 'a';
}
elsif ($nutrition_score <= 2) {
$grade = 'b';
}
elsif ($nutrition_score <= 10) {
$grade = 'c';
}
elsif ($nutrition_score <= 18) {
$grade = 'd';
}
else {
$grade = 'e';
}
}
# Other foods
else {

if ($nutrition_score <= -6) {
if ($nutrition_score <= -0) {
$grade = 'a';
}
elsif ($nutrition_score <= 2) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3404,9 +3404,8 @@
"en:nutrition-fruits-vegetables-nuts-estimate-from-ingredients",
"en:nutrition-all-nutriscore-values-known",
"en:nutrition-fruits-vegetables-legumes-estimate-from-ingredients",
"en:nutriscore-2021-different-from-2023",
"en:nutriscore-2021-better-than-2023",
"en:nutriscore-2021-a-2023-b",
"en:nutriscore-2021-same-as-2023",
"en:nutriscore-2021-a-2023-a",
"en:packagings-number-of-components-0",
"en:packagings-not-complete",
"en:packagings-empty",
Expand Down Expand Up @@ -3566,7 +3565,7 @@
"sugars" : 7.575,
"sugars_points" : 2
},
"grade" : "b",
"grade" : "a",
"nutrients_available" : 1,
"nutriscore_applicable" : 1,
"nutriscore_computed" : 1,
Expand All @@ -3577,7 +3576,7 @@
"a"
],
"nutriscore_2023_tags" : [
"b"
"a"
],
"nutriscore_data" : {
"energy" : 155.25,
Expand Down Expand Up @@ -4333,9 +4332,8 @@
"en:nutrition-fruits-vegetables-nuts-estimate-from-ingredients",
"en:nutrition-all-nutriscore-values-known",
"en:nutrition-fruits-vegetables-legumes-estimate-from-ingredients",
"en:nutriscore-2021-different-from-2023",
"en:nutriscore-2021-better-than-2023",
"en:nutriscore-2021-a-2023-b",
"en:nutriscore-2021-same-as-2023",
"en:nutriscore-2021-a-2023-a",
"en:packagings-number-of-components-0",
"en:packagings-not-complete",
"en:packagings-empty",
Expand Down Expand Up @@ -4495,7 +4493,7 @@
"sugars" : 7.575,
"sugars_points" : 2
},
"grade" : "b",
"grade" : "a",
"nutrients_available" : 1,
"nutriscore_applicable" : 1,
"nutriscore_computed" : 1,
Expand All @@ -4506,7 +4504,7 @@
"a"
],
"nutriscore_2023_tags" : [
"b"
"a"
],
"nutriscore_data" : {
"energy" : 155.25,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -620,9 +620,8 @@
"en:nutrition-fruits-vegetables-nuts-estimate-from-ingredients",
"en:nutrition-all-nutriscore-values-known",
"en:nutrition-fruits-vegetables-legumes-estimate-from-ingredients",
"en:nutriscore-2021-different-from-2023",
"en:nutriscore-2021-better-than-2023",
"en:nutriscore-2021-a-2023-b",
"en:nutriscore-2021-same-as-2023",
"en:nutriscore-2021-a-2023-a",
"en:packagings-number-of-components-0",
"en:packagings-not-complete",
"en:packagings-empty",
Expand Down Expand Up @@ -782,7 +781,7 @@
"sugars" : 7.575,
"sugars_points" : 2
},
"grade" : "b",
"grade" : "a",
"nutrients_available" : 1,
"nutriscore_applicable" : 1,
"nutriscore_computed" : 1,
Expand All @@ -793,7 +792,7 @@
"a"
],
"nutriscore_2023_tags" : [
"b"
"a"
],
"nutriscore_data" : {
"energy" : 155.25,
Expand Down Expand Up @@ -1549,9 +1548,8 @@
"en:nutrition-fruits-vegetables-nuts-estimate-from-ingredients",
"en:nutrition-all-nutriscore-values-known",
"en:nutrition-fruits-vegetables-legumes-estimate-from-ingredients",
"en:nutriscore-2021-different-from-2023",
"en:nutriscore-2021-better-than-2023",
"en:nutriscore-2021-a-2023-b",
"en:nutriscore-2021-same-as-2023",
"en:nutriscore-2021-a-2023-a",
"en:packagings-number-of-components-0",
"en:packagings-not-complete",
"en:packagings-empty",
Expand Down Expand Up @@ -1711,7 +1709,7 @@
"sugars" : 7.575,
"sugars_points" : 2
},
"grade" : "b",
"grade" : "a",
"nutrients_available" : 1,
"nutriscore_applicable" : 1,
"nutriscore_computed" : 1,
Expand All @@ -1722,7 +1720,7 @@
"a"
],
"nutriscore_2023_tags" : [
"b"
"a"
],
"nutriscore_data" : {
"energy" : 155.25,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3404,9 +3404,8 @@
"en:nutrition-fruits-vegetables-nuts-estimate-from-ingredients",
"en:nutrition-all-nutriscore-values-known",
"en:nutrition-fruits-vegetables-legumes-estimate-from-ingredients",
"en:nutriscore-2021-different-from-2023",
"en:nutriscore-2021-better-than-2023",
"en:nutriscore-2021-a-2023-b",
"en:nutriscore-2021-same-as-2023",
"en:nutriscore-2021-a-2023-a",
"en:packagings-number-of-components-0",
"en:packagings-not-complete",
"en:packagings-empty",
Expand Down Expand Up @@ -3566,7 +3565,7 @@
"sugars" : 7.575,
"sugars_points" : 2
},
"grade" : "b",
"grade" : "a",
"nutrients_available" : 1,
"nutriscore_applicable" : 1,
"nutriscore_computed" : 1,
Expand All @@ -3577,7 +3576,7 @@
"a"
],
"nutriscore_2023_tags" : [
"b"
"a"
],
"nutriscore_data" : {
"energy" : 155.25,
Expand Down Expand Up @@ -4333,9 +4332,8 @@
"en:nutrition-fruits-vegetables-nuts-estimate-from-ingredients",
"en:nutrition-all-nutriscore-values-known",
"en:nutrition-fruits-vegetables-legumes-estimate-from-ingredients",
"en:nutriscore-2021-different-from-2023",
"en:nutriscore-2021-better-than-2023",
"en:nutriscore-2021-a-2023-b",
"en:nutriscore-2021-same-as-2023",
"en:nutriscore-2021-a-2023-a",
"en:packagings-number-of-components-0",
"en:packagings-not-complete",
"en:packagings-empty",
Expand Down Expand Up @@ -4495,7 +4493,7 @@
"sugars" : 7.575,
"sugars_points" : 2
},
"grade" : "b",
"grade" : "a",
"nutrients_available" : 1,
"nutriscore_applicable" : 1,
"nutriscore_computed" : 1,
Expand All @@ -4506,7 +4504,7 @@
"a"
],
"nutriscore_2023_tags" : [
"b"
"a"
],
"nutriscore_data" : {
"energy" : 155.25,
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/expected_test_results/nutriscore/colza-oil.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@
"energy" : 3760,
"energy_from_saturated_fat" : 259,
"energy_from_saturated_fat_points" : 2,
"fat" : null,
"fat" : 100,
"fiber" : 0,
"fiber_points" : 0,
"fruits_vegetables_legumes" : null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,8 @@
"en:nutrition-fruits-vegetables-nuts-estimate-from-ingredients",
"en:nutrition-all-nutriscore-values-known",
"en:nutrition-fruits-vegetables-legumes-estimate-from-ingredients",
"en:nutriscore-2021-different-from-2023",
"en:nutriscore-2021-better-than-2023",
"en:nutriscore-2021-a-2023-b"
"en:nutriscore-2021-same-as-2023",
"en:nutriscore-2021-a-2023-a"
],
"nucleotides_tags" : [],
"nutriments" : {
Expand Down Expand Up @@ -245,7 +244,7 @@
"sugars" : 10.1,
"sugars_points" : 3
},
"grade" : "b",
"grade" : "a",
"nutrients_available" : 1,
"nutriscore_applicable" : 1,
"nutriscore_computed" : 1,
Expand All @@ -256,7 +255,7 @@
"a"
],
"nutriscore_2023_tags" : [
"b"
"a"
],
"nutriscore_data" : {
"energy" : 207,
Expand Down
Loading

0 comments on commit 4f04c19

Please sign in to comment.