Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Eco-Score improvements and change of scale: A+ to F - DRAFT #10829

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
924 changes: 924 additions & 0 deletions html/images/attributes/src/ecoscore-a-plus.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
908 changes: 908 additions & 0 deletions html/images/attributes/src/ecoscore-f.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 12 additions & 17 deletions lib/ProductOpener/Attributes.pm
Original file line number Diff line number Diff line change
Expand Up @@ -637,13 +637,7 @@ The return value is a reference to the resulting attribute data structure.
=head4 % Match

To differentiate products more finely, the match is based on the Eco-Score score
that is used to define the Eco-Score grade from A to E.

- Eco-Score A: 80 to 100
- Eco-Score B: 60 to 79
- Eco-Score C: 40 to 59
- Eco-Score D: 20 to 39
- Eco-Score E: 0 to 19
that is used to define the Eco-Score grade from A+ to F.

=cut

Expand Down Expand Up @@ -675,13 +669,8 @@ sub compute_attribute_ecoscore ($product_ref, $target_lc, $target_cc) {
if $log->is_debug();

# Compute match based on score

my $match = 0;

# Score ranges from 0 to 100 with some maluses and bonuses that can be added
# Warning: a Eco-Score score of 20 means D grade for the Eco-Score, but a match of 20 is E grade for the attributes
# So we add 1 to the Eco-Score score to compute the match.
$match = $score + 1;
# Score ranges from 0 to 100 with some maluses and bonuses that can be added or subtracted
my $match = $score;

if ($score < 0) {
$match = 0;
Expand All @@ -693,12 +682,18 @@ sub compute_attribute_ecoscore ($product_ref, $target_lc, $target_cc) {
$attribute_ref->{match} = $match;

if ($target_lc ne "data") {
my $letter_grade = uc($grade); # A+, A, B, C, D, E, F
my $grade_underscore = $grade;
$grade_underscore =~ s/\-/_/; # a-plus -> a_plus
if ($grade eq "a-plus") {
$letter_grade = "A+";
}
$attribute_ref->{title}
= sprintf(lang_in_other_lc($target_lc, "attribute_ecoscore_grade_title"), uc($grade));
= sprintf(lang_in_other_lc($target_lc, "attribute_ecoscore_grade_title"), $letter_grade);
$attribute_ref->{description}
= lang_in_other_lc($target_lc, "attribute_ecoscore_" . $grade . "_description");
= lang_in_other_lc($target_lc, "attribute_ecoscore_" . $grade_underscore . "_description");
$attribute_ref->{description_short}
= lang_in_other_lc($target_lc, "attribute_ecoscore_" . $grade . "_description_short");
= lang_in_other_lc($target_lc, "attribute_ecoscore_" . $grade_underscore . "_description_short");
}
$attribute_ref->{icon_url} = "$static_subdomain/images/attributes/dist/ecoscore-$grade.svg";
}
Expand Down
50 changes: 40 additions & 10 deletions lib/ProductOpener/Display.pm
Original file line number Diff line number Diff line change
Expand Up @@ -2038,6 +2038,17 @@ sub display_list_of_tags ($request_ref, $query_ref) {
$stats{all_tags_products} += $count;
}

# For the Eco-Score, we want to display A+ before A even though A+ is after A in alphabetical order
# If the tagid "a" is followed by tagid "a-plus", invert them
if (($tagtype eq 'ecoscore') and (defined $tags[1])) {

if (($tags[0]{_id} eq 'a') and ($tags[1]{_id} eq 'a-plus')) {
my $tags_tmp = $tags[0];
$tags[0] = $tags[1];
$tags[1] = $tags_tmp;
}
}

foreach my $tagcount_ref (@tags) {

$i++;
Expand Down Expand Up @@ -2198,9 +2209,12 @@ sub display_list_of_tags ($request_ref, $query_ref) {

my $tag_link = $main_link . $link;

$html .= "<tr><td>";
$html .= "<tr>";

my $display = '';
# For Eco-Score, we add a data-sort attribute to sort the A+ grade before the A grade in Datatables.js
my $data_sort;

my @sameAs = ();
if ($tagtype eq 'nutrition_grades') {
my $grade;
Expand All @@ -2214,29 +2228,37 @@ sub display_list_of_tags ($request_ref, $query_ref) {
$grade = lang("unknown");
}
$display
= "<img src=\"/images/attributes/dist/nutriscore-$tagid.svg\" alt=\"$Lang{nutrition_grade_fr_alt}{$lc} "
= "<img src=\"/images/attributes/dist/nutriscore-$tagid.svg\" alt=\"Nutri-Score "
. $grade
. "\" title=\"$Lang{nutrition_grade_fr_alt}{$lc} "
. "\" title=\"Nutri-Score "
. $grade
. "\" style=\"max-height:80px;\"> "
. $grade;
}
elsif ($tagtype eq 'ecoscore') {
my $grade;

if ($tagid =~ /^[abcde]$/) {
$grade = uc($tagid);
if ($tagid eq "a-plus") {
$grade = "A+";
$data_sort = "A+";
}
elsif ($tagid =~ /^[abcdef]$/) {
$grade = " " . uc($tagid);
$data_sort = "X-" . $grade;
}
elsif ($tagid eq "not-applicable") {
$grade = lang("not_applicable");
$data_sort = "Z";
}
else {
$grade = lang("unknown");
$data_sort = "Y";
}

$display
= "<img src=\"/images/attributes/dist/ecoscore-$tagid.svg\" alt=\"$Lang{ecoscore}{$lc} "
= "<img src=\"/images/attributes/dist/ecoscore-$tagid.svg\" alt=\"Eco-Score "
. $grade
. "\" title=\"$Lang{ecoscore}{$lc} "
. "\" title=\"Eco-Score "
. $grade
. "\" style=\"max-height:80px;\"> "
. $grade;
Expand Down Expand Up @@ -2270,6 +2292,13 @@ sub display_list_of_tags ($request_ref, $query_ref) {
$percent = ' (' . sprintf("%2.2f", $products / $stats{all_tags_products} * 100) . '%)';
}

if (defined $data_sort) {
$html .= "<td data-sort=\"$data_sort\">";
}
else {
$html .= "<td>";
}

$css_class =~ s/^\s+|\s+$//g;
$info .= ' class="' . $css_class . '"';
$html .= "<a href=\"$tag_link\"$info$nofollow>" . $display . "</a>";
Expand Down Expand Up @@ -2433,10 +2462,11 @@ HTML
}
}
elsif ($request_ref->{groupby_tagtype} eq 'ecoscore') {
$categories = "'A','B','C','D','E','" . lang("not_applicable") . "','" . lang("unknown") . "'";
$colors = "'#1E8F4E','#60AC0E','#EEAE0E','#FF6F1E','#DF1F1F','#a0a0a0','#a0a0a0'";
$categories
= "'A+','A','B','C','D','E',,'F','" . lang("not_applicable") . "','" . lang("unknown") . "'";
$colors = "'#1E8F4E','#1E8F4E','#60AC0E','#EEAE0E','#FF6F1E','#DF1F1F','#DF1F1F','#a0a0a0','#a0a0a0'";
$series_data = '';
foreach my $ecoscore_grade ('a', 'b', 'c', 'd', 'e', 'not-applicable', 'unknown') {
foreach my $ecoscore_grade ('a-plus', 'a', 'b', 'c', 'd', 'e', 'f', 'not-applicable', 'unknown') {
$series_data .= ($products{$ecoscore_grade} + 0) . ',';
}
}
Expand Down
48 changes: 22 additions & 26 deletions lib/ProductOpener/Ecoscore.pm
Original file line number Diff line number Diff line change
Expand Up @@ -849,39 +849,29 @@ sub compute_ecoscore ($product_ref) {

$product_ref->{ecoscore_data}{"scores"}{$cc} += $bonus;

# Assign A to E grade
# Assign A+ to F grade
# SI(AO3>=90;"A+";SI(AO3>=75;"A";SI(AO3>=60;"B";SI(AO3>=45;"C";SI(AO3>=30;"D";SI(AO3>=15;"E";"F"))))));"")

if ($product_ref->{ecoscore_data}{"scores"}{$cc} >= 80) {
if ($product_ref->{ecoscore_data}{"scores"}{$cc} >= 90) {
$product_ref->{ecoscore_data}{"grades"}{$cc} = "a-plus";
}
elsif ($product_ref->{ecoscore_data}{"scores"}{$cc} >= 75) {
$product_ref->{ecoscore_data}{"grades"}{$cc} = "a";
}
elsif ($product_ref->{ecoscore_data}{"scores"}{$cc} >= 60) {
$product_ref->{ecoscore_data}{"grades"}{$cc} = "b";
}
elsif ($product_ref->{ecoscore_data}{"scores"}{$cc} >= 40) {
elsif ($product_ref->{ecoscore_data}{"scores"}{$cc} >= 45) {
$product_ref->{ecoscore_data}{"grades"}{$cc} = "c";
}
elsif ($product_ref->{ecoscore_data}{"scores"}{$cc} >= 20) {
elsif ($product_ref->{ecoscore_data}{"scores"}{$cc} >= 30) {
$product_ref->{ecoscore_data}{"grades"}{$cc} = "d";
}
else {
elsif ($product_ref->{ecoscore_data}{"scores"}{$cc} >= 15) {
$product_ref->{ecoscore_data}{"grades"}{$cc} = "e";
}

# If a product has the grade A and it contains a non-biodegradable and non-recyclable material, downgrade to B
if (
($product_ref->{ecoscore_data}{"grades"}{$cc} eq "a")
and ($product_ref->{ecoscore_data}{adjustments}{packaging}
{non_recyclable_and_non_biodegradable_materials} > 0)
)
{
$product_ref->{ecoscore_data}{"downgraded"} = "non_recyclable_and_non_biodegradable_materials";
# For France, save the original score
if ($cc eq 'fr') {
$product_ref->{ecoscore_data}{"scores"}{$cc . "_orig"}
= $product_ref->{ecoscore_data}{"scores"}{$cc};
}
$product_ref->{ecoscore_data}{"grades"}{$cc} = "b";
$product_ref->{ecoscore_data}{"scores"}{$cc} = 79;
else {
$product_ref->{ecoscore_data}{"grades"}{$cc} = "f";
}

$log->debug(
Expand Down Expand Up @@ -1395,6 +1385,8 @@ $product_ref->{adjustments}{origins_of_ingredients} hash with:
- transportation_value_[country code]
- aggregated origins: sorted array of origin + percent to show the % of ingredients by country used in the computation

Note: the country EPI is not taken into account if the product already has a bonus for the production system.

=cut

sub compute_ecoscore_origins_of_ingredients_adjustment ($product_ref) {
Expand Down Expand Up @@ -1518,6 +1510,13 @@ sub compute_ecoscore_origins_of_ingredients_adjustment ($product_ref) {
{aggregated_origins => \@aggregated_origins})
if $log->is_debug();

# EPI score is not counted if we already have a bonus for the production system
# In this case, we set the EPI score to 0
if ($product_ref->{ecoscore_data}{adjustments}{production_system}{value} > 0) {
$epi_score = 0;
$epi_value = 0;
}

$product_ref->{ecoscore_data}{adjustments}{origins_of_ingredients} = {
origins_from_origins_field => \@origins_from_origins_field,
origins_from_categories => \@origins_from_categories,
Expand Down Expand Up @@ -1578,12 +1577,9 @@ sub compute_ecoscore_packaging_adjustment ($product_ref) {

my $warning;

# If we do not have packagings info, return the maximum malus, and indicate the product can contain non recyclable materials
# If we do not have packagings info, return the maximum malus
if ((not defined $product_ref->{packagings}) or (scalar @{$product_ref->{packagings}} == 0)) {
$product_ref->{ecoscore_data}{adjustments}{packaging} = {
value => -15,
non_recyclable_and_non_biodegradable_materials => 1,
};
$product_ref->{ecoscore_data}{adjustments}{packaging} = {value => -15,};
# indicate that we are missing key data
# this is to indicate to 3rd party that the computed Eco-Score should not be displayed without warnings
$product_ref->{ecoscore_data}{missing_key_data} = 1;
Expand Down
10 changes: 9 additions & 1 deletion po/common/common.pot
Original file line number Diff line number Diff line change
Expand Up @@ -4811,11 +4811,15 @@ msgctxt "attribute_ecoscore_setting_note"
msgid "The Eco-Score is an environmental score (ecoscore) from A to E which makes it easy to compare the impact of food products on the environment."
msgstr ""

# keep %s, it will be replaced by the letter A, B, C, D or E
# keep %s, it will be replaced by the letter A+, A, B, C, D, E or F
msgctxt "attribute_ecoscore_grade_title"
msgid "Eco-Score %s"
msgstr "Eco-Score %s"

msgctxt "attribute_ecoscore_a_plus_description_short"
msgid "Very low environmental impact"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetText will complain about duplicate ids like, and Crowdin will get confused and cross contaminate between the 2 strings

msgstr "Very low environmental impact"

msgctxt "attribute_ecoscore_a_description_short"
msgid "Very low environmental impact"
msgstr "Very low environmental impact"
Expand All @@ -4836,6 +4840,10 @@ msgctxt "attribute_ecoscore_e_description_short"
msgid "Very high environmental impact"
msgstr "Very high environmental impact"

msgctxt "attribute_ecoscore_f_description_short"
msgid "Very high environmental impact"
msgstr "Very high environmental impact"

# keep the %s, it will be replaced by an allergen
msgctxt "contains_s"
msgid "Contains: %s"
Expand Down
10 changes: 9 additions & 1 deletion po/common/en.po
Original file line number Diff line number Diff line change
Expand Up @@ -4835,11 +4835,15 @@ msgctxt "attribute_ecoscore_setting_note"
msgid "The Eco-Score is an environmental score (ecoscore) from A to E which makes it easy to compare the impact of food products on the environment."
msgstr "The Eco-Score is an environmental score (ecoscore) from A to E which makes it easy to compare the impact of food products on the environment."

# keep %s, it will be replaced by the letter A, B, C, D or E
# keep %s, it will be replaced by the letter A+, A, B, C, D, E or F
msgctxt "attribute_ecoscore_grade_title"
msgid "Eco-Score %s"
msgstr "Eco-Score %s"

msgctxt "attribute_ecoscore_a_plus_description_short"
msgid "Very low environmental impact"
msgstr "Very low environmental impact"

msgctxt "attribute_ecoscore_a_description_short"
msgid "Very low environmental impact"
msgstr "Very low environmental impact"
Expand All @@ -4860,6 +4864,10 @@ msgctxt "attribute_ecoscore_e_description_short"
msgid "Very high environmental impact"
msgstr "Very high environmental impact"

msgctxt "attribute_ecoscore_f_description_short"
msgid "Very high environmental impact"
msgstr "Very high environmental impact"

# keep the %s, it will be replaced by an allergen
msgctxt "contains_s"
msgid "Contains: %s"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@
"grade" : "c",
"icon_url" : "http://static.openfoodfacts.localhost/images/attributes/dist/ecoscore-c.svg",
"id" : "ecoscore",
"match" : 48,
"match" : 47,
"name" : "Eco-Score",
"panel_id" : "ecoscore",
"status" : "known",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@
"grade" : "c",
"icon_url" : "http://static.openfoodfacts.localhost/images/attributes/dist/ecoscore-c.svg",
"id" : "ecoscore",
"match" : 48,
"match" : 47,
"name" : "Eco-Score",
"panel_id" : "ecoscore",
"status" : "known",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@
"grade" : "c",
"icon_url" : "http://static.openfoodfacts.localhost/images/attributes/dist/ecoscore-c.svg",
"id" : "ecoscore",
"match" : 48,
"match" : 47,
"name" : "Eco-Score",
"panel_id" : "ecoscore",
"status" : "known",
Expand Down Expand Up @@ -633,7 +633,7 @@
"grade" : "c",
"icon_url" : "http://static.openfoodfacts.localhost/images/attributes/dist/ecoscore-c.svg",
"id" : "ecoscore",
"match" : 48,
"match" : 47,
"name" : "Eco-Score",
"panel_id" : "ecoscore",
"status" : "known",
Expand Down
Loading
Loading