diff --git a/lib/ProductOpener/Attributes.pm b/lib/ProductOpener/Attributes.pm index c946935691e9f..f6fba10e55679 100644 --- a/lib/ProductOpener/Attributes.pm +++ b/lib/ProductOpener/Attributes.pm @@ -75,6 +75,9 @@ use ProductOpener::Lang qw/:all/; use ProductOpener::Display qw/:all/; use ProductOpener::Ecoscore qw/:all/; +use Data::DeepAccess qw(deep_get); + + =head1 CONFIGURATION =head2 Attribute groups and attributes @@ -463,7 +466,7 @@ sub compute_attribute_nutriscore($$) { my $attribute_ref = initialize_attribute($attribute_id, $target_lc); # Nutri-Score A, B, C, D or E - if (defined $product_ref->{nutriscore_data}) { + if ((defined $product_ref->{nutriscore_grade}) and ($product_ref->{nutriscore_grade} =~ /^[a-e]$/)) { $attribute_ref->{status} = "known"; my $nutriscore_data_ref = $product_ref->{nutriscore_data}; @@ -543,13 +546,17 @@ sub compute_attribute_nutriscore($$) { } # Nutri-Score not-applicable: alcoholic beverages, baby food etc. - elsif (has_tag($product_ref,"misc","en:nutriscore-not-applicable")) { + elsif (has_tag($product_ref,"nutrition_grades","not-applicable")) { $attribute_ref->{status} = "known"; $attribute_ref->{icon_url} = "$static_subdomain/images/attributes/nutriscore-not-applicable.svg"; $attribute_ref->{match} = 0; if ($target_lc ne "data") { - $attribute_ref->{title} = lang_in_other_lc($target_lc, "attribute_nutriscore_not_applicable_title"); - $attribute_ref->{description} = lang_in_other_lc($target_lc, "attribute_nutriscore_not_applicable_description"); + $attribute_ref->{title} = lang_in_other_lc($target_lc, "attribute_nutriscore_not_applicable_title"); + $attribute_ref->{description} = f_lang_in_lc($target_lc, "f_attribute_nutriscore_not_applicable_description", { + category => display_taxonomy_tag_name($target_lc, "categories", + deep_get($product_ref, qw/nutriscore_data nutriscore_not_applicable_for_category/)) + } + ); $attribute_ref->{description_short} = lang_in_other_lc($target_lc, "attribute_nutriscore_not_applicable_description_short"); } } @@ -656,6 +663,22 @@ sub compute_attribute_ecoscore($$$) { } $attribute_ref->{icon_url} = "$static_subdomain/images/attributes/ecoscore-$grade.svg"; } + # Eco-Score is not-applicable + elsif ((defined $product_ref->{ecoscore_grade}) and ($product_ref->{ecoscore_grade} eq "not-applicable")) { + $attribute_ref->{status} = "unknown"; + $attribute_ref->{icon_url} = "$static_subdomain/images/attributes/ecoscore-unknown.svg"; + $attribute_ref->{match} = 0; + if ($target_lc ne "data") { + $attribute_ref->{title} = lang_in_other_lc($target_lc, "attribute_ecoscore_not_applicable_title"); + $attribute_ref->{description} = f_lang_in_lc($target_lc, "f_attribute_ecoscore_not_applicable_description", { + category => display_taxonomy_tag_name($target_lc, "categories", + deep_get($product_ref, qw/ecoscore_data ecoscore_not_applicable_for_category/)) + } + ); + $attribute_ref->{description_short} = lang_in_other_lc($target_lc, "attribute_ecoscore_not_applicable_description_short"); + } + } + # Eco-Score is unknown else { $attribute_ref->{status} = "unknown"; $attribute_ref->{icon_url} = "$static_subdomain/images/attributes/ecoscore-unknown.svg"; @@ -663,7 +686,7 @@ sub compute_attribute_ecoscore($$$) { if ($target_lc ne "data") { $attribute_ref->{title} = lang_in_other_lc($target_lc, "attribute_ecoscore_unknown_title"); $attribute_ref->{description} = lang_in_other_lc($target_lc, "attribute_ecoscore_unknown_description"); - $attribute_ref->{description_short} = lang_in_other_lc($target_lc, "attribute_ecoscore_unknown_description_short"); + $attribute_ref->{description_short} = lang_in_other_lc($target_lc, "attribute_ecoscore_unknown_description_short"); } } diff --git a/lib/ProductOpener/Display.pm b/lib/ProductOpener/Display.pm index 4b1edc16c21d9..9c51e2b7f247a 100644 --- a/lib/ProductOpener/Display.pm +++ b/lib/ProductOpener/Display.pm @@ -8863,7 +8863,11 @@ sub data_to_display_nutriscore_and_nutrient_levels($) { push @nutriscore_warnings, lang("nutriscore_not_applicable"); $result_data_ref->{nutriscore_grade} = "not-applicable"; $result_data_ref->{nutriscore_unknown_reason} = "not_applicable"; - $result_data_ref->{nutriscore_unknown_reason_short} = lang("nutriscore_not_applicable_short"); + $result_data_ref->{nutriscore_unknown_reason_short} = f_lang("f_attribute_nutriscore_not_applicable_description", { + category => display_taxonomy_tag_name($lc, "categories", + deep_get($product_ref, qw/nutriscore_data nutriscore_not_applicable_for_category/)) + } + ); } else { @@ -8895,7 +8899,8 @@ sub data_to_display_nutriscore_and_nutrient_levels($) { $result_data_ref->{nutriscore_warnings} = \@nutriscore_warnings; } - if (defined $product_ref->{nutriscore_data}) { + # Display the details of the computation of the Nutri-Score if we computed one + if ((defined $product_ref->{nutriscore_grade}) and ($product_ref->{nutriscore_grade} =~ /^[a-e]$/)) { $result_data_ref->{nutriscore_details} = display_nutriscore_calculation_details($product_ref->{nutriscore_data}); } diff --git a/lib/ProductOpener/Food.pm b/lib/ProductOpener/Food.pm index 8cad2ba0893cc..85e42547e7357 100644 --- a/lib/ProductOpener/Food.pm +++ b/lib/ProductOpener/Food.pm @@ -1572,7 +1572,8 @@ sub compute_nutrition_score($) { if (has_tag($product_ref, "categories", $category_id)) { $product_ref->{"nutrition_grades_tags"} = [ "not-applicable" ]; add_tag($product_ref,"misc","en:nutriscore-not-applicable"); - $product_ref->{nutrition_score_debug} = "no nutriscore for category $category_id" . " - ";; + $product_ref->{nutrition_score_debug} = "no nutriscore for category $category_id" . " - "; + $product_ref->{nutriscore_data} = {nutriscore_not_applicable_for_category => $category_id}; last; } } diff --git a/lib/ProductOpener/KnowledgePanels.pm b/lib/ProductOpener/KnowledgePanels.pm index c639c3343ce88..6d112b8112c50 100644 --- a/lib/ProductOpener/KnowledgePanels.pm +++ b/lib/ProductOpener/KnowledgePanels.pm @@ -70,6 +70,7 @@ use ProductOpener::PackagerCodes qw/:all/; use JSON::PP; use Encode; +use Data::DeepAccess qw(deep_get); =head1 FUNCTIONS @@ -555,6 +556,18 @@ sub create_ecoscore_panel($$$) { create_panel_from_json_template("ecoscore_total", "api/knowledge-panels/environment/ecoscore/total.tt.json", $panel_data_ref, $product_ref, $target_lc, $target_cc); } + # Eco-Score is not applicable + elsif ((defined $product_ref->{ecoscore_grade}) and ($product_ref->{ecoscore_grade} eq "not-applicable")) { + my $panel_data_ref = {}; + $panel_data_ref->{subtitle} = f_lang_in_lc($target_lc, "f_attribute_ecoscore_not_applicable_description", { + category => display_taxonomy_tag_name($target_lc, "categories", + deep_get($product_ref, qw/ecoscore_data ecoscore_not_applicable_for_category/)) + } + ); + create_panel_from_json_template("ecoscore", "api/knowledge-panels/environment/ecoscore/ecoscore_not_applicable.tt.json", + $panel_data_ref, $product_ref, $target_lc, $target_cc); + } + # Eco-Score is unknown else { my $panel_data_ref = {}; create_panel_from_json_template("ecoscore", "api/knowledge-panels/environment/ecoscore/ecoscore_unknown.tt.json", @@ -792,8 +805,13 @@ sub create_nutriscore_panel($$$) { my $panel_data_ref = data_to_display_nutriscore_and_nutrient_levels($product_ref); - $panel_data_ref->{title} = lang_in_other_lc($target_lc, "attribute_nutriscore_" . $panel_data_ref->{nutriscore_grade} . "_description_short"); - + if ($panel_data_ref->{nutriscore_grade} eq "not-applicable") { + $panel_data_ref->{title} = lang_in_other_lc($target_lc, "attribute_nutriscore_not_applicable_title"); + } + else { + $panel_data_ref->{title} = lang_in_other_lc($target_lc, "attribute_nutriscore_" . $panel_data_ref->{nutriscore_grade} . "_description_short"); + } + # Nutri-Score panel: score + details create_panel_from_json_template("nutriscore", "api/knowledge-panels/health/nutriscore/nutriscore.tt.json", $panel_data_ref, $product_ref, $target_lc, $target_cc); diff --git a/lib/ProductOpener/Lang.pm b/lib/ProductOpener/Lang.pm index c2863f6faf634..c407910ac5936 100644 --- a/lib/ProductOpener/Lang.pm +++ b/lib/ProductOpener/Lang.pm @@ -59,6 +59,7 @@ BEGIN &lang &lang_sprintf &f_lang + &f_lang_in_lc &lang_in_other_lc %lang_lc @@ -210,6 +211,10 @@ If a translation is not available, the function returns English. In the .po translation files, we use the msgctxt field for the string id. +=head4 variables hash reference $variables_ref + +Reference to a hash that contains values for the variables that will be replaced. + =cut sub f_lang($$) { @@ -217,7 +222,46 @@ sub f_lang($$) { my $stringid = shift; my $variables_ref = shift; - my $translation = lang($stringid); + return f_lang_in_lc($lc, $stringid, $variables_ref); +} + + +=head2 f_lang_in_lc ( $target_lc, $stringid, $variables_ref ) + +Returns a translation for a specific string id with specific arguments +in the language $target_lc. + +The translation is stored using Python's f-string format with +named parameters between { }. + +e.g. "This is a string with {a_variable} and {another_variable}." + +Variables between { } are interpolated with the corresponding entry +in the $variables_ref hash reference. + +If a translation is not available, the function returns English. + +=head3 Arguments + + =head4 target language $target_lc + +=head4 string id $stringid + +In the .po translation files, we use the msgctxt field for the string id. + +=head4 variables hash reference $variables_ref + +Reference to a hash that contains values for the variables that will be replaced. + +=cut + +sub f_lang_in_lc($$$) { + + my $target_lc = shift; + my $stringid = shift; + my $variables_ref = shift; + + my $translation = $Lang{$stringid}{$target_lc}; if (defined $translation) { # look for string keys between { } and replace them with the corresponding # value in $variables_ref hash reference diff --git a/po/common/common.pot b/po/common/common.pot index 68633e2fe5068..e85217202a889 100644 --- a/po/common/common.pot +++ b/po/common/common.pot @@ -4652,8 +4652,13 @@ msgid "Nutri-Score not-applicable" msgstr "Nutri-Score not-applicable" msgctxt "attribute_nutriscore_not_applicable_description_short" -msgid "Exempted category" -msgstr "Exempted category" +msgid "Not-applicable for the category" +msgstr "Not-applicable for the category" + +# variable names between { } must not be translated +msgctxt "f_attribute_nutriscore_not_applicable_description" +msgid "Not-applicable for the category: {category}" +msgstr "Not-applicable for the category: {category}" msgctxt "attribute_nutriscore_a_description_short" msgid "Very good nutritional quality" @@ -5365,6 +5370,23 @@ msgctxt "delete_user_process" msgid "User deleted." msgstr "User deleted." +msgctxt "attribute_ecoscore_not_applicable_title" +msgid "Eco-Score not yet applicable" +msgstr "Eco-Score not yet applicable" + +msgctxt "attribute_ecoscore_not_applicable_description_short" +msgid "Not yet applicable for the category" +msgstr "Not yet applicable for the category" + +# variable names between { } must not be translated +msgctxt "f_attribute_ecoscore_not_applicable_description" +msgid "Not yet applicable for the category: {category}" +msgstr "Not yet applicable for the category: {category}" + +msgctxt "ecoscore_not_applicable_coming_soon" +msgid "The Eco-Score is not yet applicable for this category, but we are working on adding support for it." +msgstr "The Eco-Score is not yet applicable for this category, but we are working on adding support for it." + msgctxt "attribute_ecoscore_unknown_title" msgid "Eco-Score not computed" msgstr "Eco-Score not computed" @@ -5984,10 +6006,6 @@ msgctxt "f_energy_expenditure_for_weight_in_kg_lb" msgid "Energy expenditure for a person weighting {kg} kg / {lb} lb" msgstr "Energy expenditure for a person weighting {kg} kg / {lb} lb" -msgctxt "nutriscore_not_applicable_short" -msgid "Not applicable for this product" -msgstr "Not applicable for this product" - msgctxt "nutriscore_missing_category_short" msgid "Missing category" msgstr "Missing category" diff --git a/po/common/en.po b/po/common/en.po index 79dac2a26fb5b..475f6db53683c 100644 --- a/po/common/en.po +++ b/po/common/en.po @@ -4672,8 +4672,13 @@ msgid "Nutri-Score not-applicable" msgstr "Nutri-Score not-applicable" msgctxt "attribute_nutriscore_not_applicable_description_short" -msgid "Exempted category" -msgstr "Exempted category" +msgid "Not-applicable for the category" +msgstr "Not-applicable for the category" + +# variable names between { } must not be translated +msgctxt "f_attribute_nutriscore_not_applicable_description" +msgid "Not-applicable for the category: {category}" +msgstr "Not-applicable for the category: {category}" msgctxt "attribute_nutriscore_a_description_short" msgid "Very good nutritional quality" @@ -4845,6 +4850,23 @@ msgctxt "attribute_ecoscore_grade_title" msgid "Eco-Score %s" msgstr "Eco-Score %s" +msgctxt "attribute_ecoscore_not_applicable_title" +msgid "Eco-Score not yet applicable" +msgstr "Eco-Score not yet applicable" + +msgctxt "attribute_ecoscore_not_applicable_description_short" +msgid "Not yet applicable for the category" +msgstr "Not yet applicable for the category" + +# variable names between { } must not be translated +msgctxt "f_attribute_ecoscore_not_applicable_description" +msgid "Not yet applicable for the category: {category}" +msgstr "Not yet applicable for the category: {category}" + +msgctxt "ecoscore_not_applicable_coming_soon" +msgid "The Eco-Score is not yet applicable for this category, but we are working on adding support for it." +msgstr "The Eco-Score is not yet applicable for this category, but we are working on adding support for it." + msgctxt "attribute_ecoscore_unknown_title" msgid "Eco-Score not computed" msgstr "Eco-Score not computed" @@ -6006,10 +6028,6 @@ msgctxt "f_energy_expenditure_for_weight_in_kg_lb" msgid "Energy expenditure for a person weighting {kg} kg / {lb} lb" msgstr "Energy expenditure for a person weighting {kg} kg / {lb} lb" -msgctxt "nutriscore_not_applicable_short" -msgid "Not applicable for this product" -msgstr "Not applicable for this product" - msgctxt "nutriscore_missing_category_short" msgid "Missing category" msgstr "Missing category" diff --git a/po/common/fr.po b/po/common/fr.po index 3fa184eacfc73..9ee17ab909dca 100644 --- a/po/common/fr.po +++ b/po/common/fr.po @@ -4513,8 +4513,13 @@ msgid "Nutri-Score not-applicable" msgstr "Nutri-Score non applicable" msgctxt "attribute_nutriscore_not_applicable_description_short" -msgid "Exempted category" -msgstr "Catégorie exemptée" +msgid "Not-applicable for the category" +msgstr "Non applicable pour la catégorie" + +# variable names between { } must not be translated +msgctxt "f_attribute_nutriscore_not_applicable_description" +msgid "Not-applicable for the category: {category}" +msgstr "Non applicable pour la catégorie : {category}" msgctxt "attribute_nutriscore_a_description_short" msgid "Very good nutritional quality" @@ -5222,6 +5227,23 @@ msgctxt "delete_user_process" msgid "User deleted." msgstr "Utilisateur supprimé." +msgctxt "attribute_ecoscore_not_applicable_title" +msgid "Eco-Score not yet applicable" +msgstr "Eco-Score pas encore applicable" + +msgctxt "attribute_ecoscore_not_applicable_description_short" +msgid "Not yet applicable for the category" +msgstr "Pas encore applicable pour la catégorie" + +# variable names between { } must not be translated +msgctxt "f_attribute_ecoscore_not_applicable_description" +msgid "Not yet applicable for the category: {category}" +msgstr "Pas encore applicable pour la catégorie : {category}" + +msgctxt "ecoscore_not_applicable_coming_soon" +msgid "The Eco-Score is not yet applicable for this category, but we are working on adding support for it." +msgstr "L'Eco-Score n'est pas encore applicable pour cette catégorie, mais nous travaillons pour l'ajouter." + msgctxt "attribute_ecoscore_unknown_title" msgid "Eco-Score not computed" msgstr "Eco-Score non calculé" diff --git a/t/ecoscore.t b/t/ecoscore.t index a09568b4a112b..3a3b0be649fcb 100644 --- a/t/ecoscore.t +++ b/t/ecoscore.t @@ -110,6 +110,13 @@ my @tests = ( categories_tags=>["en:butters"], } ], + [ + 'exempted-category-sodas', + { + lc => "en", + categories_tags=>["en:sodas"], + } + ], [ 'label-organic', { diff --git a/t/expected_test_results/ecoscore/exempted-category-sodas.json b/t/expected_test_results/ecoscore/exempted-category-sodas.json new file mode 100644 index 0000000000000..c4c34f073b5f8 --- /dev/null +++ b/t/expected_test_results/ecoscore/exempted-category-sodas.json @@ -0,0 +1,21 @@ +{ + "categories_tags" : [ + "en:sodas" + ], + "ecoscore_data" : { + "adjustments" : {}, + "ecoscore_not_applicable_for_category" : "en:sodas", + "status" : "unknown" + }, + "ecoscore_grade" : "not-applicable", + "ecoscore_tags" : [ + "not-applicable" + ], + "lc" : "en", + "misc_tags" : [ + "en:ecoscore-extended-data-not-computed", + "en:ecoscore-not-applicable", + "en:ecoscore-not-computed" + ], + "packagings" : [] +} diff --git a/t/expected_test_results/nutriscore/en-beers-category.json b/t/expected_test_results/nutriscore/en-beers-category.json new file mode 100644 index 0000000000000..654524aa69729 --- /dev/null +++ b/t/expected_test_results/nutriscore/en-beers-category.json @@ -0,0 +1,63 @@ +{ + "categories" : "beers", + "categories_hierarchy" : [ + "en:beverages", + "en:alcoholic-beverages", + "en:beers" + ], + "categories_lc" : "en", + "categories_properties" : { + "agribalyse_proxy_food_code:en" : "5000" + }, + "categories_properties_tags" : [ + "all-products", + "categories-known", + "agribalyse-food-code-unknown", + "agribalyse-proxy-food-code-5000", + "agribalyse-proxy-food-code-known", + "ciqual-food-code-unknown", + "agribalyse-known", + "agribalyse-5000" + ], + "categories_tags" : [ + "en:beverages", + "en:alcoholic-beverages", + "en:beers" + ], + "food_groups" : "en:alcoholic-beverages", + "food_groups_tags" : [ + "en:alcoholic-beverages" + ], + "lc" : "en", + "misc_tags" : [ + "en:nutriscore-not-computed", + "en:nutriscore-not-applicable" + ], + "nutriments" : { + "energy_100g" : 182, + "fat_100g" : 0, + "fiber_100g" : 0.5, + "proteins_100g" : 0.2, + "saturated-fat_100g" : 0, + "sodium_100g" : 0.2, + "sugars_100g" : 8.9 + }, + "nutriscore_data" : { + "nutriscore_not_applicable_for_category" : "en:alcoholic-beverages" + }, + "nutrition_grades_tags" : [ + "not-applicable" + ], + "nutrition_score_beverage" : 1, + "nutrition_score_debug" : "no nutriscore for category en:alcoholic-beverages", + "pnns_groups_1" : "Alcoholic beverages", + "pnns_groups_1_tags" : [ + "alcoholic-beverages", + "known" + ], + "pnns_groups_2" : "Alcoholic beverages", + "pnns_groups_2_tags" : [ + "alcoholic-beverages", + "known" + ] +} diff --git a/t/nutriscore.t b/t/nutriscore.t index e9e3978dfbb35..0e3411d2ff907 100644 --- a/t/nutriscore.t +++ b/t/nutriscore.t @@ -114,6 +114,17 @@ my @tests = ( } ], +# categories without Nutri-Score + +[ + "en-beers-category", + { + lc => "en", + categories => "beers", + nutriments=>{energy_100g=>182, fat_100g=>0, "saturated-fat_100g"=>0, sugars_100g=>8.9, sodium_100g=>0.2, fiber_100g=>0.5, proteins_100g=>0.2}, + } +], + ); diff --git a/templates/api/knowledge-panels/environment/ecoscore/ecoscore_not_applicable.tt.json b/templates/api/knowledge-panels/environment/ecoscore/ecoscore_not_applicable.tt.json new file mode 100644 index 0000000000000..0efe14e5aa5b6 --- /dev/null +++ b/templates/api/knowledge-panels/environment/ecoscore/ecoscore_not_applicable.tt.json @@ -0,0 +1,22 @@ +{ + "level": "info", + "topics": [ + "environment" + ], + "title_element": { + "icon_url": "[% static_subdomain %]/images/attributes/ecoscore-unknown.svg", + "title": "[% lang("attribute_ecoscore_not_applicable_title") %]", + "subtitle": "[% panel.subtitle %]", + "type": "grade", + "grade": "unknown", + }, + "elements": [ + { + "element_type": "text", + "text_element": { + "type": "summary", + "html": "[% lang("ecoscore_not_applicable_coming_soon") %]" + } + }, + ] +}