diff --git a/lib/ProductOpener/Ecoscore.pm b/lib/ProductOpener/Ecoscore.pm index 38f5996072a7d..594c35d7b26c5 100644 --- a/lib/ProductOpener/Ecoscore.pm +++ b/lib/ProductOpener/Ecoscore.pm @@ -73,6 +73,7 @@ use ProductOpener::Config qw/:all/; use ProductOpener::Store qw/:all/; use ProductOpener::Tags qw/:all/; use ProductOpener::Packaging qw/:all/; +use ProductOpener::Ingredients qw/:all/; use Storable qw(dclone freeze); use Text::CSV(); @@ -1161,7 +1162,17 @@ sub compute_ecoscore_origins_of_ingredients_adjustment($) { } } - if (scalar @origins_from_origins_field == 0) { + # If we don't have ingredients, check if we have an origin for a specific ingredient + # (e.g. we have the label "French eggs" even though we don't have ingredients) + if ((scalar @origins_from_origins_field == 0) + and ((not defined $product_ref->{ingredients}) or (scalar @{$product_ref->{ingredients}} == 0))) { + my $origin_id = has_specific_ingredient_property($product_ref, undef, "origins"); + if ((defined $origin_id) and (defined $ecoscore_data{origins}{$origin_id})) { + push @origins_from_origins_field, $origin_id; + } + } + + if (scalar @origins_from_origins_field == 0) { @origins_from_origins_field = ("en:unknown"); } @@ -1177,6 +1188,7 @@ sub compute_ecoscore_origins_of_ingredients_adjustment($) { else { # If we don't have ingredients listed, apply the origins from the origins field # using a dummy ingredient + aggregate_origins_of_ingredients(\@origins_from_origins_field, \%aggregated_origins , [ { percent_estimate => 100} ]); } @@ -1479,9 +1491,7 @@ sub localize_ecoscore ($$) { $origin_ref->{transportation_score} = $ecoscore_data{origins}{$origin_id}{"transportation_score_" . $cc}; } } - } - } } diff --git a/lib/ProductOpener/Ingredients.pm b/lib/ProductOpener/Ingredients.pm index 23b851323618b..2af48d3c09c23 100644 --- a/lib/ProductOpener/Ingredients.pm +++ b/lib/ProductOpener/Ingredients.pm @@ -98,6 +98,8 @@ BEGIN &add_milk &estimate_milk_percent_from_ingredients + &has_specific_ingredient_property + ); # symbols to export on request %EXPORT_TAGS = (all => [@EXPORT_OK]); } @@ -863,13 +865,161 @@ my %ignore_strings_after_percent = ( ); +=head2 has_specific_ingredient_property ( product_ref, searched_ingredient_id, property ) + +Check if the specific ingredients structure (extracted from the end of the ingredients list and product labels) +contains a property for an ingredient. (e.g. do we have an origin specified for a specific ingredient) + +=head3 Arguments + +=head4 product_ref + +=head4 searched_ingredient_id + +If the ingredient_id parameter is undef, then we return the value for any specific ingredient. +(useful for products for which we do not have ingredients, but for which we have a label like "French eggs": +we can still derive the origin of the ingredients, e.g. for the Eco-Score) + +=head4 property + +e.g. "origins" + +=head3 Return values + +=head4 value + +- undef if we don't have a specific ingredient with the requested property matching the requested ingredient +- otherwise the value for the matching specific ingredient + +=cut + +sub has_specific_ingredient_property($$$) { + + my $product_ref = shift; + my $searched_ingredient_id = shift; + my $property = shift; + + my $value; + + # If we have specific ingredients, check if we have a parent of searched ingredient + if (defined $product_ref->{specific_ingredients}) { + foreach my $specific_ingredient_ref (@{$product_ref->{specific_ingredients}}) { + my $specific_ingredient_id = $specific_ingredient_ref->{id}; + if ((defined $specific_ingredient_ref->{$property}) # we have a value for the property for the specific ingredient + # and we did not target a specific ingredient, or this is equivalent to the searched ingredient + and ((not defined $searched_ingredient_id) or (is_a("ingredients", $searched_ingredient_id, $specific_ingredient_id)))) { + + if (not defined $value) { + $value = $specific_ingredient_ref->{$property}; + } + elsif ($specific_ingredient_ref->{$property} ne $value) { + $log->warn("has_specific_ingredient_property: different values for property", { searched_ingredient_id => $searched_ingredient_id, property => $property, current_value => $value, specific_ingredient_id => $specific_ingredient_id, new_value => $specific_ingredient_ref->{$property}}) if $log->is_warn(); + } + } + } + } + + return $value; +} + + +=head2 add_properties_from_specific_ingredients ( product_ref ) + +Go through the ingredients structure, and ad properties to ingredients that match specific ingredients +for which we have extra information (e.g. origins from a label). + +=cut + +sub add_properties_from_specific_ingredients($) { + + my $product_ref = shift; + + # Traverse the ingredients tree, breadth 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 $property (qw(origins)) { + my $property_value = has_specific_ingredient_property($product_ref, $ingredientid, "origins"); + if ((defined $property_value) and (not defined $ingredient_ref->{$property})) { + $ingredient_ref->{$property} = $property_value; + } + } + } +} + -=head2 parse_specific_ingredients_text ( product_ref, $text ) +=head2 add_specific_ingredients_from_labels ( product_ref ) + +Check if the product has labels that indicate properties (e.g. origins) for specific ingredients. + +e.g. + +en:French pork +fr:Viande Porcine Française, VPF, viande de porc française, Le Porc Français, Porc Origine France, porc français, porc 100% France +origins:en: en:france +ingredients:en: en:pork + +This function extracts those mentions and adds them to the specific_ingredients structure. + +=head3 Return values + +=head4 specific_ingredients structure + +Array of specific ingredients. + +=head4 + +=cut + +sub add_specific_ingredients_from_labels($) { + + my $product_ref = shift; + + my $product_lc = $product_ref->{lc}; + + if (defined $product_ref->{labels_tags}) { + foreach my $labelid (@{$product_ref->{labels_tags}}) { + my $ingredients = get_property("labels", $labelid, "ingredients:en"); + if (defined $ingredients) { + my $origins = get_property("labels", $labelid, "origins:en"); + + if (defined $origins) { + + my $ingredient_id = canonicalize_taxonomy_tag("en", "ingredients", $ingredients); + + my $specific_ingredients_ref = { + id => $ingredient_id, + ingredient => $ingredients, + label => $labelid, + origins => join(",", map {canonicalize_taxonomy_tag("en", "origins", $_)} split(/,/, $origins )) + }; + + push @{$product_ref->{specific_ingredients}}, $specific_ingredients_ref; + } + } + } + } +} + + +=head2 parse_specific_ingredients_from_text ( product_ref, $text ) Lists of ingredients sometime include extra mentions for specific ingredients at the end of the ingredients list. e.g. "Prepared with 50g of fruits for 100g of finished product". -This function extracts those mentions and adds them to a special specific_ingredients structure. +This function extracts those mentions and adds them to the specific_ingredients structure. =head3 Return values @@ -881,7 +1031,7 @@ Array of specific ingredients. =cut -sub parse_specific_ingredients_text($$$) { +sub parse_specific_ingredients_from_text($$$) { my $product_ref = shift; my $text = shift; @@ -889,8 +1039,6 @@ sub parse_specific_ingredients_text($$$) { my $product_lc = $product_ref->{lc}; - $product_ref->{specific_ingredients} = []; - # Go through the ingredient lists multiple times # as long as we have one match my $ingredient = "start"; @@ -901,7 +1049,7 @@ sub parse_specific_ingredients_text($$$) { $ingredient = undef; my $matched_text; my $percent; - my $origin; + my $origins; # Note: in regular expressions below, use non-capturing groups (starting with (?: ) # for all groups, except groups that capture actual data: ingredient name, percent, origins @@ -925,7 +1073,7 @@ sub parse_specific_ingredients_text($$$) { # Note: the regexp above does not currently match multiple origins with commas (e.g. "Origins of milk: UK, UE") # in order to not overmatch something like "Origin of milk: UK, some other mention." # In the future, we could try to be smarter and match more if we can recognize the next words exist in the origins taxonomy. - $origin = $2; + $origins = $2; $ingredient = $1; $matched_text = $&; # Remove the matched text @@ -963,7 +1111,7 @@ sub parse_specific_ingredients_text($$$) { # Note: the regexp above does not currently match multiple origins with commas (e.g. "Origins of milk: UK, UE") # in order to not overmatch something like "Origin of milk: UK, some other mention." # In the future, we could try to be smarter and match more if we can recognize the next words exist in the origins taxonomy. - $origin = $2; + $origins = $2; $ingredient = $1; $matched_text = $&; # Remove the matched text @@ -985,17 +1133,12 @@ sub parse_specific_ingredients_text($$$) { }; defined $percent and $specific_ingredients_ref->{percent} = $percent; - defined $origin and $specific_ingredients_ref->{origin} = join(",", map {canonicalize_taxonomy_tag($product_lc, "origins", $_)} split(/,/, $origin )); + defined $origins and $specific_ingredients_ref->{origins} = join(",", map {canonicalize_taxonomy_tag($product_lc, "origins", $_)} split(/,/, $origins )); push @{$product_ref->{specific_ingredients}}, $specific_ingredients_ref; } } - # Delete specific ingredients if empty - if (scalar @{$product_ref->{specific_ingredients}} == 0) { - delete $product_ref->{specific_ingredients}; - } - return $text; } @@ -1049,6 +1192,7 @@ sub parse_ingredients_text($) { # remove ending . and ending whitespaces $text =~ s/(\s|\.)+$//; + # initialize the structure to store the parsed ingredients and specific ingredients $product_ref->{ingredients} = []; # farine (12%), chocolat (beurre de cacao (15%), sucre [10%], protéines de lait, oeuf 1%) - émulsifiants : E463, E432 et E472 - correcteurs d'acidité : E322/E333 E474-E475, acidifiant (acide citrique, acide phosphorique) - sel : 1% ... @@ -1075,7 +1219,7 @@ sub parse_ingredients_text($) { my $percent_regexp = '(?:<|' . $min_regexp . '|\s|\.|:)*(\d+(?:(?:\,|\.)\d+)?)\s*(?:\%|g)\s*(?:' . $min_regexp . '|' . $ignore_strings_after_percent . '|\s|\)|\]|\}|\*)*'; # Extract phrases related to specific ingredients at the end of the ingredients list - $text = parse_specific_ingredients_text($product_ref, $text, $percent_regexp); + $text = parse_specific_ingredients_from_text($product_ref, $text, $percent_regexp); my $analyze_ingredients_function = sub($$$$) { @@ -1930,12 +2074,7 @@ sub compute_ingredients_tags($) { # Traverse the ingredients tree, breadth first - my @ingredients = (); - - for (my $i = 0; $i < @{$product_ref->{ingredients}}; $i++) { - - push @ingredients, $product_ref->{ingredients}[$i]; - } + my @ingredients = @{$product_ref->{ingredients}}; while (@ingredients) { @@ -1945,10 +2084,7 @@ sub compute_ingredients_tags($) { if (defined $ingredient_ref->{ingredients}) { - for (my $i = 0; $i < @{$ingredient_ref->{ingredients}}; $i++) { - - push @ingredients, $ingredient_ref->{ingredients}[$i]; - } + push @ingredients, @{$ingredient_ref->{ingredients}}; } else { # Count specified percent only for ingredients that do not have sub ingredients @@ -2028,10 +2164,18 @@ sub extract_ingredients_from_text($) { # Parse the ingredients list to extract individual ingredients and sub-ingredients # to create the ingredients array with nested sub-ingredients arrays + $product_ref->{specific_ingredients} = []; + parse_ingredients_text($product_ref); + # Add specific ingredients from labels + add_specific_ingredients_from_labels($product_ref); + if (defined $product_ref->{ingredients}) { + # Add properties like origins from specific ingredients extracted from labels or the end of the ingredients list + add_properties_from_specific_ingredients($product_ref); + # Compute minimum and maximum percent ranges for each ingredient and sub ingredient if (compute_ingredients_percent_values(100, 100, $product_ref->{ingredients}) < 0) { @@ -2060,6 +2204,11 @@ sub extract_ingredients_from_text($) { analyze_ingredients($product_ref); + # Delete specific ingredients if empty + if ((exists $product_ref->{specific_ingredients}) and (scalar @{$product_ref->{specific_ingredients}} == 0)) { + delete $product_ref->{specific_ingredients}; + } + return; } @@ -2522,12 +2671,7 @@ sub analyze_ingredients($) { # Traverse the ingredients tree, breadth first - my @ingredients = (); - - for (my $i = 0; $i < @{$product_ref->{ingredients}}; $i++) { - - push @ingredients, $product_ref->{ingredients}[$i]; - } + my @ingredients = @{$product_ref->{ingredients}}; while (@ingredients) { diff --git a/t/ecoscore.t b/t/ecoscore.t index e674ee39376c6..a09568b4a112b 100644 --- a/t/ecoscore.t +++ b/t/ecoscore.t @@ -519,6 +519,28 @@ my @tests = ( }, ], + # Labels that indicate the origin of some ingredients + [ + "fr-viande-porcine-francaise", + { + lc => "fr", + categories => "endives au jambon", + ingredients_text => "endives 40%, jambon cuit, jaunes d'oeufs, sel", + labels => "viande porcine française, oeufs de France", + } + ], + + # Label that indicates the origin of an ingredient, but no ingredient list + # (common for products with only 1 ingredient like eggs) + [ + "fr-oeufs-de-france", + { + lc => "fr", + categories => "oeufs", + labels => "oeufs de France", + } + ], + ); @@ -530,6 +552,14 @@ foreach my $test_ref (@tests) { my $product_ref = $test_ref->[1]; # Run the test + + if (defined $product_ref->{labels}) { + compute_field_tags($product_ref, $product_ref->{lc}, "labels"); + } + + if (defined $product_ref->{categories}) { + compute_field_tags($product_ref, $product_ref->{lc}, "categories"); + } # Parse the ingredients (and extract the origins), and compute the ingredients percent extract_ingredients_from_text($product_ref); diff --git a/t/expected_test_results/ecoscore/fr-oeufs-de-france.json b/t/expected_test_results/ecoscore/fr-oeufs-de-france.json new file mode 100644 index 0000000000000..0afec39500cf6 --- /dev/null +++ b/t/expected_test_results/ecoscore/fr-oeufs-de-france.json @@ -0,0 +1,430 @@ +{ + "categories" : "oeufs", + "categories_hierarchy" : [ + "en:farming-products", + "en:eggs" + ], + "categories_lc" : "fr", + "categories_tags" : [ + "en:farming-products", + "en:eggs" + ], + "ecoscore_data" : { + "adjustments" : { + "origins_of_ingredients" : { + "aggregated_origins" : [ + { + "origin" : "en:france", + "percent" : 100 + } + ], + "epi_score" : 93, + "epi_value" : 4, + "origins_from_origins_field" : [ + "en:france" + ], + "transportation_scores" : { + "ad" : 57, + "al" : 0, + "at" : 38, + "ax" : 67, + "ba" : 14, + "be" : 85, + "bg" : 21, + "ch" : 69, + "cy" : 40, + "cz" : 48, + "de" : 61, + "dk" : 39, + "dz" : 45, + "ee" : 71, + "eg" : 35, + "es" : 37, + "fi" : 69, + "fo" : 62, + "fr" : 100, + "gg" : 78, + "gi" : 4, + "gr" : 49, + "hr" : 30, + "hu" : 26, + "ie" : 47, + "il" : 34, + "im" : 50, + "is" : 53, + "it" : 47, + "je" : 76, + "lb" : 39, + "li" : 64, + "lt" : 63, + "lu" : 82, + "lv" : 71, + "ly" : 56, + "ma" : 60, + "mc" : 52, + "md" : 29, + "me" : 37, + "mk" : 29, + "mt" : 57, + "nl" : 77, + "no" : 20, + "pl" : 25, + "ps" : 42, + "pt" : 13, + "ro" : 31, + "rs" : 7, + "se" : 15, + "si" : 38, + "sj" : 53, + "sk" : 24, + "sm" : 40, + "sy" : 26, + "tn" : 9, + "tr" : 7, + "ua" : 40, + "uk" : 68, + "us" : 0, + "va" : 29, + "world" : 0, + "xk" : 28 + }, + "transportation_values" : { + "ad" : 9, + "al" : 0, + "at" : 6, + "ax" : 10, + "ba" : 2, + "be" : 13, + "bg" : 3, + "ch" : 10, + "cy" : 6, + "cz" : 7, + "de" : 9, + "dk" : 6, + "dz" : 7, + "ee" : 11, + "eg" : 5, + "es" : 6, + "fi" : 10, + "fo" : 9, + "fr" : 15, + "gg" : 12, + "gi" : 1, + "gr" : 7, + "hr" : 5, + "hu" : 4, + "ie" : 7, + "il" : 5, + "im" : 8, + "is" : 8, + "it" : 7, + "je" : 11, + "lb" : 6, + "li" : 10, + "lt" : 9, + "lu" : 12, + "lv" : 11, + "ly" : 8, + "ma" : 9, + "mc" : 8, + "md" : 4, + "me" : 6, + "mk" : 4, + "mt" : 9, + "nl" : 12, + "no" : 3, + "pl" : 4, + "ps" : 6, + "pt" : 2, + "ro" : 5, + "rs" : 1, + "se" : 2, + "si" : 6, + "sj" : 8, + "sk" : 4, + "sm" : 6, + "sy" : 4, + "tn" : 1, + "tr" : 1, + "ua" : 6, + "uk" : 10, + "us" : 0, + "va" : 4, + "world" : 0, + "xk" : 4 + }, + "values" : { + "ad" : 13, + "al" : 4, + "at" : 10, + "ax" : 14, + "ba" : 6, + "be" : 17, + "bg" : 7, + "ch" : 14, + "cy" : 10, + "cz" : 11, + "de" : 13, + "dk" : 10, + "dz" : 11, + "ee" : 15, + "eg" : 9, + "es" : 10, + "fi" : 14, + "fo" : 13, + "fr" : 19, + "gg" : 16, + "gi" : 5, + "gr" : 11, + "hr" : 9, + "hu" : 8, + "ie" : 11, + "il" : 9, + "im" : 12, + "is" : 12, + "it" : 11, + "je" : 15, + "lb" : 10, + "li" : 14, + "lt" : 13, + "lu" : 16, + "lv" : 15, + "ly" : 12, + "ma" : 13, + "mc" : 12, + "md" : 8, + "me" : 10, + "mk" : 8, + "mt" : 13, + "nl" : 16, + "no" : 7, + "pl" : 8, + "ps" : 10, + "pt" : 6, + "ro" : 9, + "rs" : 5, + "se" : 6, + "si" : 10, + "sj" : 12, + "sk" : 8, + "sm" : 10, + "sy" : 8, + "tn" : 5, + "tr" : 5, + "ua" : 10, + "uk" : 14, + "us" : 4, + "va" : 8, + "world" : 4, + "xk" : 8 + } + }, + "packaging" : { + "non_recyclable_and_non_biodegradable_materials" : 1, + "value" : -15, + "warning" : "packaging_data_missing" + }, + "production_system" : { + "labels" : [], + "value" : 0, + "warning" : "no_label" + }, + "threatened_species" : { + "warning" : "ingredients_missing" + } + }, + "agribalyse" : { + "agribalyse_proxy_food_code" : "22000", + "co2_agriculture" : "1.9896005", + "co2_consumption" : "0.099564814", + "co2_distribution" : "0.026120269", + "co2_packaging" : "0.12348329", + "co2_processing" : "0", + "co2_total" : "2.4091714", + "co2_transportation" : "0.17040258", + "code" : "22000", + "dqr" : "2.43", + "ef_agriculture" : "0.48629013", + "ef_consumption" : "0.0042189169", + "ef_distribution" : "0.0092457532", + "ef_packaging" : "0.018717616", + "ef_processing" : "0", + "ef_total" : 0.53172645, + "ef_transportation" : "0.013254034", + "is_beverage" : 0, + "name_en" : "Egg, raw", + "name_fr" : "Oeuf, cru", + "score" : 51 + }, + "grade" : "c", + "grades" : { + "ad" : "c", + "al" : "c", + "at" : "c", + "ax" : "c", + "ba" : "c", + "be" : "c", + "bg" : "c", + "ch" : "c", + "cy" : "c", + "cz" : "c", + "de" : "c", + "dk" : "c", + "dz" : "c", + "ee" : "c", + "eg" : "c", + "es" : "c", + "fi" : "c", + "fo" : "c", + "fr" : "c", + "gg" : "c", + "gi" : "c", + "gr" : "c", + "hr" : "c", + "hu" : "c", + "ie" : "c", + "il" : "c", + "im" : "c", + "is" : "c", + "it" : "c", + "je" : "c", + "lb" : "c", + "li" : "c", + "lt" : "c", + "lu" : "c", + "lv" : "c", + "ly" : "c", + "ma" : "c", + "mc" : "c", + "md" : "c", + "me" : "c", + "mk" : "c", + "mt" : "c", + "nl" : "c", + "no" : "c", + "pl" : "c", + "ps" : "c", + "pt" : "c", + "ro" : "c", + "rs" : "c", + "se" : "c", + "si" : "c", + "sj" : "c", + "sk" : "c", + "sm" : "c", + "sy" : "c", + "tn" : "c", + "tr" : "c", + "ua" : "c", + "uk" : "c", + "us" : "c", + "va" : "c", + "world" : "c", + "xk" : "c" + }, + "missing" : { + "ingredients" : 1, + "labels" : 1, + "packagings" : 1 + }, + "missing_data_warning" : 1, + "score" : 55, + "scores" : { + "ad" : 49, + "al" : 40, + "at" : 46, + "ax" : 50, + "ba" : 42, + "be" : 53, + "bg" : 43, + "ch" : 50, + "cy" : 46, + "cz" : 47, + "de" : 49, + "dk" : 46, + "dz" : 47, + "ee" : 51, + "eg" : 45, + "es" : 46, + "fi" : 50, + "fo" : 49, + "fr" : 55, + "gg" : 52, + "gi" : 41, + "gr" : 47, + "hr" : 45, + "hu" : 44, + "ie" : 47, + "il" : 45, + "im" : 48, + "is" : 48, + "it" : 47, + "je" : 51, + "lb" : 46, + "li" : 50, + "lt" : 49, + "lu" : 52, + "lv" : 51, + "ly" : 48, + "ma" : 49, + "mc" : 48, + "md" : 44, + "me" : 46, + "mk" : 44, + "mt" : 49, + "nl" : 52, + "no" : 43, + "pl" : 44, + "ps" : 46, + "pt" : 42, + "ro" : 45, + "rs" : 41, + "se" : 42, + "si" : 46, + "sj" : 48, + "sk" : 44, + "sm" : 46, + "sy" : 44, + "tn" : 41, + "tr" : 41, + "ua" : 46, + "uk" : 50, + "us" : 40, + "va" : 44, + "world" : 40, + "xk" : 44 + }, + "status" : "known" + }, + "ecoscore_grade" : "c", + "ecoscore_score" : 55, + "ecoscore_tags" : [ + "c" + ], + "labels" : "oeufs de France", + "labels_hierarchy" : [ + "en:french-eggs" + ], + "labels_lc" : "fr", + "labels_tags" : [ + "en:french-eggs" + ], + "lc" : "fr", + "misc_tags" : [ + "en:ecoscore-extended-data-not-computed", + "en:ecoscore-missing-data-warning", + "en:ecoscore-missing-data-labels", + "en:ecoscore-missing-data-packagings", + "en:ecoscore-missing-data-no-packagings", + "en:ecoscore-computed" + ], + "packagings" : [], + "specific_ingredients" : [ + { + "id" : "en:egg", + "ingredient" : "en:egg", + "label" : "en:french-eggs", + "origins" : "en:france" + } + ] +} diff --git a/t/expected_test_results/ecoscore/fr-viande-porcine-francaise.json b/t/expected_test_results/ecoscore/fr-viande-porcine-francaise.json new file mode 100644 index 0000000000000..b9ab92c437ff0 --- /dev/null +++ b/t/expected_test_results/ecoscore/fr-viande-porcine-francaise.json @@ -0,0 +1,551 @@ +{ + "categories" : "endives au jambon", + "categories_hierarchy" : [ + "en:meals", + "en:endives-with-ham" + ], + "categories_lc" : "fr", + "categories_tags" : [ + "en:meals", + "en:endives-with-ham" + ], + "ecoscore_data" : { + "adjustments" : { + "origins_of_ingredients" : { + "aggregated_origins" : [ + { + "origin" : "en:france", + "percent" : 50 + }, + { + "origin" : "en:unknown", + "percent" : 50 + } + ], + "epi_score" : 46.5, + "epi_value" : 0, + "origins_from_origins_field" : [ + "en:unknown" + ], + "transportation_scores" : { + "ad" : 28.5, + "al" : 0, + "at" : 19, + "ax" : 33.5, + "ba" : 7, + "be" : 42.5, + "bg" : 10.5, + "ch" : 34.5, + "cy" : 20, + "cz" : 24, + "de" : 30.5, + "dk" : 19.5, + "dz" : 22.5, + "ee" : 35.5, + "eg" : 17.5, + "es" : 18.5, + "fi" : 34.5, + "fo" : 31, + "fr" : 50, + "gg" : 39, + "gi" : 2, + "gr" : 24.5, + "hr" : 15, + "hu" : 13, + "ie" : 23.5, + "il" : 17, + "im" : 25, + "is" : 26.5, + "it" : 23.5, + "je" : 38, + "lb" : 19.5, + "li" : 32, + "lt" : 31.5, + "lu" : 41, + "lv" : 35.5, + "ly" : 28, + "ma" : 30, + "mc" : 26, + "md" : 14.5, + "me" : 18.5, + "mk" : 14.5, + "mt" : 28.5, + "nl" : 38.5, + "no" : 10, + "pl" : 12.5, + "ps" : 21, + "pt" : 6.5, + "ro" : 15.5, + "rs" : 3.5, + "se" : 7.5, + "si" : 19, + "sj" : 26.5, + "sk" : 12, + "sm" : 20, + "sy" : 13, + "tn" : 4.5, + "tr" : 3.5, + "ua" : 20, + "uk" : 34, + "us" : 0, + "va" : 14.5, + "world" : 0, + "xk" : 14 + }, + "transportation_values" : { + "ad" : 4, + "al" : 0, + "at" : 3, + "ax" : 5, + "ba" : 1, + "be" : 6, + "bg" : 2, + "ch" : 5, + "cy" : 3, + "cz" : 4, + "de" : 5, + "dk" : 3, + "dz" : 3, + "ee" : 5, + "eg" : 3, + "es" : 3, + "fi" : 5, + "fo" : 5, + "fr" : 8, + "gg" : 6, + "gi" : 0, + "gr" : 4, + "hr" : 2, + "hu" : 2, + "ie" : 4, + "il" : 3, + "im" : 4, + "is" : 4, + "it" : 4, + "je" : 6, + "lb" : 3, + "li" : 5, + "lt" : 5, + "lu" : 6, + "lv" : 5, + "ly" : 4, + "ma" : 5, + "mc" : 4, + "md" : 2, + "me" : 3, + "mk" : 2, + "mt" : 4, + "nl" : 6, + "no" : 2, + "pl" : 2, + "ps" : 3, + "pt" : 1, + "ro" : 2, + "rs" : 1, + "se" : 1, + "si" : 3, + "sj" : 4, + "sk" : 2, + "sm" : 3, + "sy" : 2, + "tn" : 1, + "tr" : 1, + "ua" : 3, + "uk" : 5, + "us" : 0, + "va" : 2, + "world" : 0, + "xk" : 2 + }, + "values" : { + "ad" : 4, + "al" : 0, + "at" : 3, + "ax" : 5, + "ba" : 1, + "be" : 6, + "bg" : 2, + "ch" : 5, + "cy" : 3, + "cz" : 4, + "de" : 5, + "dk" : 3, + "dz" : 3, + "ee" : 5, + "eg" : 3, + "es" : 3, + "fi" : 5, + "fo" : 5, + "fr" : 8, + "gg" : 6, + "gi" : 0, + "gr" : 4, + "hr" : 2, + "hu" : 2, + "ie" : 4, + "il" : 3, + "im" : 4, + "is" : 4, + "it" : 4, + "je" : 6, + "lb" : 3, + "li" : 5, + "lt" : 5, + "lu" : 6, + "lv" : 5, + "ly" : 4, + "ma" : 5, + "mc" : 4, + "md" : 2, + "me" : 3, + "mk" : 2, + "mt" : 4, + "nl" : 6, + "no" : 2, + "pl" : 2, + "ps" : 3, + "pt" : 1, + "ro" : 2, + "rs" : 1, + "se" : 1, + "si" : 3, + "sj" : 4, + "sk" : 2, + "sm" : 3, + "sy" : 2, + "tn" : 1, + "tr" : 1, + "ua" : 3, + "uk" : 5, + "us" : 0, + "va" : 2, + "world" : 0, + "xk" : 2 + } + }, + "packaging" : { + "non_recyclable_and_non_biodegradable_materials" : 1, + "value" : -15, + "warning" : "packaging_data_missing" + }, + "production_system" : { + "labels" : [], + "value" : 0, + "warning" : "no_label" + }, + "threatened_species" : {} + }, + "agribalyse" : { + "agribalyse_food_code" : "25073", + "co2_agriculture" : "2.5456413", + "co2_consumption" : "0.012726077", + "co2_distribution" : "0.030685111", + "co2_packaging" : "0.33707484", + "co2_processing" : "0.30446436", + "co2_total" : "3.4538682", + "co2_transportation" : "0.22294599", + "code" : "25073", + "dqr" : "1.97", + "ef_agriculture" : "0.3294875", + "ef_consumption" : "0.0064417627", + "ef_distribution" : "0.0089786658", + "ef_packaging" : "0.027385171", + "ef_processing" : "0.069171047", + "ef_total" : 0.45864946, + "ef_transportation" : "0.017143605", + "is_beverage" : 0, + "name_en" : "Chicory w ham", + "name_fr" : "Endive au jambon", + "score" : 57 + }, + "grade" : "c", + "grades" : { + "ad" : "c", + "al" : "c", + "at" : "c", + "ax" : "c", + "ba" : "c", + "be" : "c", + "bg" : "c", + "ch" : "c", + "cy" : "c", + "cz" : "c", + "de" : "c", + "dk" : "c", + "dz" : "c", + "ee" : "c", + "eg" : "c", + "es" : "c", + "fi" : "c", + "fo" : "c", + "fr" : "c", + "gg" : "c", + "gi" : "c", + "gr" : "c", + "hr" : "c", + "hu" : "c", + "ie" : "c", + "il" : "c", + "im" : "c", + "is" : "c", + "it" : "c", + "je" : "c", + "lb" : "c", + "li" : "c", + "lt" : "c", + "lu" : "c", + "lv" : "c", + "ly" : "c", + "ma" : "c", + "mc" : "c", + "md" : "c", + "me" : "c", + "mk" : "c", + "mt" : "c", + "nl" : "c", + "no" : "c", + "pl" : "c", + "ps" : "c", + "pt" : "c", + "ro" : "c", + "rs" : "c", + "se" : "c", + "si" : "c", + "sj" : "c", + "sk" : "c", + "sm" : "c", + "sy" : "c", + "tn" : "c", + "tr" : "c", + "ua" : "c", + "uk" : "c", + "us" : "c", + "va" : "c", + "world" : "c", + "xk" : "c" + }, + "missing" : { + "labels" : 1, + "packagings" : 1 + }, + "missing_data_warning" : 1, + "score" : 50, + "scores" : { + "ad" : 46, + "al" : 42, + "at" : 45, + "ax" : 47, + "ba" : 43, + "be" : 48, + "bg" : 44, + "ch" : 47, + "cy" : 45, + "cz" : 46, + "de" : 47, + "dk" : 45, + "dz" : 45, + "ee" : 47, + "eg" : 45, + "es" : 45, + "fi" : 47, + "fo" : 47, + "fr" : 50, + "gg" : 48, + "gi" : 42, + "gr" : 46, + "hr" : 44, + "hu" : 44, + "ie" : 46, + "il" : 45, + "im" : 46, + "is" : 46, + "it" : 46, + "je" : 48, + "lb" : 45, + "li" : 47, + "lt" : 47, + "lu" : 48, + "lv" : 47, + "ly" : 46, + "ma" : 47, + "mc" : 46, + "md" : 44, + "me" : 45, + "mk" : 44, + "mt" : 46, + "nl" : 48, + "no" : 44, + "pl" : 44, + "ps" : 45, + "pt" : 43, + "ro" : 44, + "rs" : 43, + "se" : 43, + "si" : 45, + "sj" : 46, + "sk" : 44, + "sm" : 45, + "sy" : 44, + "tn" : 43, + "tr" : 43, + "ua" : 45, + "uk" : 47, + "us" : 42, + "va" : 44, + "world" : 42, + "xk" : 44 + }, + "status" : "known" + }, + "ecoscore_grade" : "c", + "ecoscore_score" : 50, + "ecoscore_tags" : [ + "c" + ], + "ingredients" : [ + { + "id" : "en:belgian-endive", + "percent" : 40, + "percent_estimate" : 40, + "percent_max" : 40, + "percent_min" : 40, + "text" : "endives", + "vegan" : "yes", + "vegetarian" : "yes" + }, + { + "id" : "en:cooked-ham", + "origins" : "en:france", + "percent_estimate" : 30, + "percent_max" : 40, + "percent_min" : 20, + "text" : "jambon cuit", + "vegan" : "no", + "vegetarian" : "no" + }, + { + "id" : "en:egg-yolk", + "origins" : "en:france", + "percent_estimate" : 20, + "percent_max" : 33.3333333333333, + "percent_min" : 10, + "text" : "jaunes d'oeufs", + "vegan" : "no", + "vegetarian" : "yes" + }, + { + "id" : "en:salt", + "percent_estimate" : 10, + "percent_max" : 20, + "percent_min" : 0, + "text" : "sel", + "vegan" : "yes", + "vegetarian" : "yes" + } + ], + "ingredients_analysis" : { + "en:non-vegan" : [ + "en:cooked-ham", + "en:egg-yolk" + ], + "en:non-vegetarian" : [ + "en:cooked-ham" + ] + }, + "ingredients_analysis_tags" : [ + "en:palm-oil-free", + "en:non-vegan", + "en:non-vegetarian" + ], + "ingredients_hierarchy" : [ + "en:belgian-endive", + "en:vegetable", + "en:cooked-ham", + "en:animal", + "en:meat", + "en:pork", + "en:pork-meat", + "en:ham", + "en:egg-yolk", + "en:egg", + "en:salt" + ], + "ingredients_n" : 4, + "ingredients_n_tags" : [ + "4", + "1-10" + ], + "ingredients_original_tags" : [ + "en:belgian-endive", + "en:cooked-ham", + "en:egg-yolk", + "en:salt" + ], + "ingredients_percent_analysis" : 1, + "ingredients_tags" : [ + "en:belgian-endive", + "en:vegetable", + "en:cooked-ham", + "en:animal", + "en:meat", + "en:pork", + "en:pork-meat", + "en:ham", + "en:egg-yolk", + "en:egg", + "en:salt" + ], + "ingredients_text" : "endives 40%, jambon cuit, jaunes d'oeufs, sel", + "ingredients_with_specified_percent_n" : 1, + "ingredients_with_specified_percent_sum" : 40, + "ingredients_with_unspecified_percent_n" : 3, + "ingredients_with_unspecified_percent_sum" : 60, + "known_ingredients_n" : 11, + "labels" : "viande porcine française, oeufs de France", + "labels_hierarchy" : [ + "en:french-meat", + "en:french-eggs", + "en:french-pork" + ], + "labels_lc" : "fr", + "labels_tags" : [ + "en:french-meat", + "en:french-eggs", + "en:french-pork" + ], + "lc" : "fr", + "misc_tags" : [ + "en:ecoscore-extended-data-not-computed", + "en:ecoscore-missing-data-warning", + "en:ecoscore-missing-data-labels", + "en:ecoscore-missing-data-packagings", + "en:ecoscore-missing-data-no-packagings", + "en:ecoscore-computed" + ], + "nutriments" : { + "fruits-vegetables-nuts-estimate-from-ingredients_100g" : 40, + "fruits-vegetables-nuts-estimate-from-ingredients_serving" : 40 + }, + "packagings" : [], + "specific_ingredients" : [ + { + "id" : "en:meat", + "ingredient" : "en:meat", + "label" : "en:french-meat", + "origins" : "en:france" + }, + { + "id" : "en:egg", + "ingredient" : "en:egg", + "label" : "en:french-eggs", + "origins" : "en:france" + }, + { + "id" : "en:pork", + "ingredient" : "en:pork", + "label" : "en:french-pork", + "origins" : "en:france" + } + ], + "unknown_ingredients_n" : 0 +} diff --git a/t/expected_test_results/ingredients/en-specific-ingredients-multiple-strings-of-one-ingredient.json b/t/expected_test_results/ingredients/en-specific-ingredients-multiple-strings-of-one-ingredient.json index 7d631682626ba..75076841d4f23 100644 --- a/t/expected_test_results/ingredients/en-specific-ingredients-multiple-strings-of-one-ingredient.json +++ b/t/expected_test_results/ingredients/en-specific-ingredients-multiple-strings-of-one-ingredient.json @@ -2,6 +2,7 @@ "ingredients" : [ { "id" : "en:milk", + "origins" : "en:united-kingdom", "percent_estimate" : 66.6666666666667, "percent_max" : 100, "percent_min" : 33.3333333333333, @@ -87,7 +88,7 @@ { "id" : "en:milk", "ingredient" : "milk", - "origin" : "en:united-kingdom", + "origins" : "en:united-kingdom", "text" : "Origin of milk: UK" } ], diff --git a/t/expected_test_results/ingredients/fr-specific-ingredients.json b/t/expected_test_results/ingredients/fr-specific-ingredients.json index b133c446ab8d8..b410b03ac65b3 100644 --- a/t/expected_test_results/ingredients/fr-specific-ingredients.json +++ b/t/expected_test_results/ingredients/fr-specific-ingredients.json @@ -13,6 +13,7 @@ { "id" : "en:apricot", "labels" : "en:organic", + "origins" : "en:provence", "percent_estimate" : 18.75, "percent_max" : 50, "percent_min" : 0, @@ -159,7 +160,7 @@ { "id" : "en:apricot", "ingredient" : "abricots", - "origin" : "en:provence", + "origins" : "en:provence", "text" : "Origine des abricots: Provence." } ], diff --git a/t/expected_test_results/ingredients/fr-viande-porcine-francaise.json b/t/expected_test_results/ingredients/fr-viande-porcine-francaise.json new file mode 100644 index 0000000000000..a0b276daafe95 --- /dev/null +++ b/t/expected_test_results/ingredients/fr-viande-porcine-francaise.json @@ -0,0 +1,139 @@ +{ + "ingredients" : [ + { + "id" : "en:belgian-endive", + "percent" : 40, + "percent_estimate" : 40, + "percent_max" : 40, + "percent_min" : 40, + "text" : "endives", + "vegan" : "yes", + "vegetarian" : "yes" + }, + { + "id" : "en:cooked-ham", + "origins" : "en:france", + "percent_estimate" : 30, + "percent_max" : 40, + "percent_min" : 20, + "text" : "jambon cuit", + "vegan" : "no", + "vegetarian" : "no" + }, + { + "id" : "en:egg-yolk", + "origins" : "en:france", + "percent_estimate" : 20, + "percent_max" : 33.3333333333333, + "percent_min" : 10, + "text" : "jaunes d'oeufs", + "vegan" : "no", + "vegetarian" : "yes" + }, + { + "id" : "en:salt", + "percent_estimate" : 10, + "percent_max" : 20, + "percent_min" : 0, + "text" : "sel", + "vegan" : "yes", + "vegetarian" : "yes" + } + ], + "ingredients_analysis" : { + "en:non-vegan" : [ + "en:cooked-ham", + "en:egg-yolk" + ], + "en:non-vegetarian" : [ + "en:cooked-ham" + ] + }, + "ingredients_analysis_tags" : [ + "en:palm-oil-free", + "en:non-vegan", + "en:non-vegetarian" + ], + "ingredients_hierarchy" : [ + "en:belgian-endive", + "en:vegetable", + "en:cooked-ham", + "en:animal", + "en:meat", + "en:pork", + "en:pork-meat", + "en:ham", + "en:egg-yolk", + "en:egg", + "en:salt" + ], + "ingredients_n" : 4, + "ingredients_n_tags" : [ + "4", + "1-10" + ], + "ingredients_original_tags" : [ + "en:belgian-endive", + "en:cooked-ham", + "en:egg-yolk", + "en:salt" + ], + "ingredients_percent_analysis" : 1, + "ingredients_tags" : [ + "en:belgian-endive", + "en:vegetable", + "en:cooked-ham", + "en:animal", + "en:meat", + "en:pork", + "en:pork-meat", + "en:ham", + "en:egg-yolk", + "en:egg", + "en:salt" + ], + "ingredients_text" : "endives 40%, jambon cuit, jaunes d'oeufs, sel", + "ingredients_with_specified_percent_n" : 1, + "ingredients_with_specified_percent_sum" : 40, + "ingredients_with_unspecified_percent_n" : 3, + "ingredients_with_unspecified_percent_sum" : 60, + "known_ingredients_n" : 11, + "labels" : "viande porcine française, oeufs de France", + "labels_hierarchy" : [ + "en:french-meat", + "en:french-eggs", + "en:french-pork" + ], + "labels_lc" : "fr", + "labels_tags" : [ + "en:french-meat", + "en:french-eggs", + "en:french-pork" + ], + "lc" : "fr", + "nutriments" : { + "fruits-vegetables-nuts-estimate-from-ingredients_100g" : 40, + "fruits-vegetables-nuts-estimate-from-ingredients_serving" : 40 + }, + "specific_ingredients" : [ + { + "id" : "en:meat", + "ingredient" : "en:meat", + "label" : "en:french-meat", + "origins" : "en:france" + }, + { + "id" : "en:egg", + "ingredient" : "en:egg", + "label" : "en:french-eggs", + "origins" : "en:france" + }, + { + "id" : "en:pork", + "ingredient" : "en:pork", + "label" : "en:french-pork", + "origins" : "en:france" + } + ], + "unknown_ingredients_n" : 0 +} diff --git a/t/ingredients.t b/t/ingredients.t index 5acf029b786e8..224106955edc9 100755 --- a/t/ingredients.t +++ b/t/ingredients.t @@ -396,7 +396,17 @@ Teneur en citron de 5,5%", lc => "en", ingredients_text => "Milk, cream, sugar. Total milk content: 88%. Origin of milk: UK", }, - ] + ], + + # Labels that indicate the origin of some ingredients + [ + "fr-viande-porcine-francaise", + { + lc => "fr", + ingredients_text => "endives 40%, jambon cuit, jaunes d'oeufs, sel", + labels => "viande porcine française, oeufs de France", + } + ] ); @@ -410,6 +420,10 @@ foreach my $test_ref (@tests) { my $product_ref = $test_ref->[1]; # Run the test + + if (defined $product_ref->{labels}) { + compute_field_tags($product_ref, $product_ref->{lc}, "labels"); + } extract_ingredients_from_text($product_ref); diff --git a/taxonomies/labels.result.sto b/taxonomies/labels.result.sto index e46fa895ae8e4..aa5e56584c5f1 100644 Binary files a/taxonomies/labels.result.sto and b/taxonomies/labels.result.sto differ diff --git a/taxonomies/labels.result.txt b/taxonomies/labels.result.txt index 2716336f8f93a..0cf24347d0647 100644 --- a/taxonomies/labels.result.txt +++ b/taxonomies/labels.result.txt @@ -6,7 +6,7 @@ stopwords:de:Zutaten, von, mit, enthält, kontrolliert, zertifiziert, geprüft, stopwords:en:ingredients, agricultural, from, with, of, by, the, verified, certified, approved, 100%, pure, guaranty, guaranteed, product guaranteed, product certified, standard, during, period, phase, label, mark, logo stopwords:es:por, la, las, los, el, apto, para, verificado, certificado, aprovado, controlado, registrado, 100%, garantía, garantizado, garantizada, estándar stopwords:fi:ainesosat, vahvistettu, sertifioitu, hyväksytty, 100%, puhdas, tuote, standardi, valvottu, valvottua, tuotettu -stopwords:fr:ingrédient, ingrédients, agricole, agricoles, issu, issue, issus, issues, du, de, l', le, la, convient, peut convenir, au, aux, régime, régimes, conforme, validé, vérifié, certifié, par, un, une, approuvé, 100%, pur, garantie, garanti, produit garanti, produit certifié, sas, en, durant, pendant, phase, période, dès, label, marque, logo, produit issu, produits issus, tous, les, sont, est, issus de la production +stopwords:fr:ingrédient, ingrédients, agricole, agricoles, issu, issue, issus, issues, d', du, de, l', le, la, convient, peut convenir, au, aux, régime, régimes, conforme, validé, vérifié, certifié, par, un, une, approuvé, 100%, pur, garantie, garanti, produit garanti, produit certifié, sas, en, durant, pendant, phase, période, dès, label, marque, logo, produit issu, produits issus, tous, les, sont, est, issus de la production stopwords:hu:összetevők, mezőgazdaság, ellenőrzött, tanúsított, 100%, természetes, termék, garantált stopwords:ru:ингредиенты, из, с, проверено, сертифицировано, подтверждено, 100%, чистый, продукт, гарантия, гарантировано, стандарт stopwords:sv:certifierad, ingrediens, ingredienser, godkänd, råvara, enlighet, med, kriterierna, produkt, från, kontrollerat, kontrollerade, jordbruk, lantbruk, odling, odlingar @@ -251,6 +251,7 @@ fi:omenia Ranskasta, omenien alkuperä Ranska fr:Pommes de France it:Mele francesi pt:Maçãs da França +ingredients:en: en:apple origins:en: en:france en:Assured Food Standards, assured food standard, Red Tractor @@ -883,8 +884,9 @@ en:Egg laid in France ca:Ous posats a França de:in Frankreich gelegte Eier fi:munittu Ranskassa -fr:Pondu en France, Pondue en France, Pondues en France, Pondus en France +fr:Pondu en France, Pondue en France, Pondues en France, Pondus en France, oeufs pondus en France it:Uova deposte in Francia +ingredients:en: en:egg label_categories:en: en:Geographic label origins:en: en:france @@ -13615,6 +13617,10 @@ fr:Commerce équitable Bio, Commerce équitable et Bio, Issus du commerce équit < en:Organic fr:Bio-équitable, bioéquitable +< en:Fair trade +< en:Organic +fr:Bio équitable en France + < en:Fair trade < en:Organic fr:Biopartenaire, Bio partenaire @@ -13738,6 +13744,7 @@ fi:ranskalaisia munia, munien alkuperä Ranska fr:Oeufs de France, Oeufs français he:ביצים צרפתיות, ביצים מצרפת it:Uova francesi, Uova dalla Francia +ingredients:en: en:egg origins:en: en:france en:French meat @@ -13745,10 +13752,11 @@ ca:Carn francesa de:Fleisch aus Frankreich es:Carne francesa fi:Ranskalaista lihaa, lihan alkuperä Ranska -fr:Viande Française, VF +fr:Viande Française, VF, viandes françaises, viande origine France, viandes origine France he:בשר צרפתי it:Carne francese nl:Frans vlees +ingredients:en: en:meat origins:en: en:france < en:French meat @@ -13766,6 +13774,7 @@ fr:Canard Français, Canard de France, Canard Origine France, Viande de canard f he:ברווז צרפתי it:Anatra francese pt:Pato francês +ingredients:en: en:duck origins:en: en:france < en:French meat @@ -13791,18 +13800,22 @@ origins:en: en:france < en:French meat fr:Lapin Français, Lapin de France, Lapin Origine France, Viande de lapin français, Viande de lapin française, Viande de Lapin origine France +ingredients:en: en:rabbit origins:en: en:france < en:French meat fr:Poulet Français, Viande de poulet origine France, Poulet 100% Français, Viande de poulet français, Viande de poulet française, Poulet Origine France, Poulet 100% France +ingredients:en: en:chicken origins:en: en:france < en:French meat fr:Viande Chevaline Française, Viande chevaline origine France, Cheval 100% Français, Viande de cheval française, Viande de cheval français, Cheval Origine France, Cheval 100% France +ingredients:en: en:horse origins:en: en:france < en:French meat fr:Viande d'agneau français, VAF, Viande d'agneau Origine France, Agneau 100% Français, Viande d'agneau Française, Agneau 100% France, Agneau Origine France +ingredients:en: en:lamb origins:en: en:france < en:French meat @@ -13815,10 +13828,12 @@ origins:en: en:france < en:French meat fr:Viande de veau français, Viande de veau Origine France, Veau 100% Français, Viande de veau Française, Veau 100% France, Veau Origine France +ingredients:en: en:veal origins:en: en:france < en:French meat fr:Viande Ovine Française, Viande ovine Origine France, Ovin 100% Français, Viande d'ovin Française, Ovin 100% France, ovin Origine France +ingredients:en: en:sheep origins:en: en:france en:French milk @@ -13826,11 +13841,12 @@ ca:Llet francesa de:Milch aus Frankreich es:Leche francesa fi:ranskalaista maitoa, maidon alkuperä Ranska -fr:Lait Français, Lait de France, Lait Origine France +fr:Lait Français, Lait de France, Lait Origine France, lait collecté et conditionné en France he:חלב צרפתי it:Latte francese nl:Franse melk pt:Leite francês +ingredients:en: en:milk origins:en: en:france en:FSC, Forest Stewardship Council @@ -14264,6 +14280,7 @@ de:Honig aus Frankreich fi:hunajaa Ranskasta, hunajan alkuperä Ranska fr:Miel de France, miel français he:דבש מצרפת +ingredients:en: en:honey label_categories:en: en:Honey origins:en: en:france @@ -16593,6 +16610,7 @@ fi:perunoita Ranskasta, perunoiden alkuperä Ranska fr:Pommes de terre de France it:Patate francesi, Patate dalla Francia nl:Aardappelen uit Frankrijk +ingredients:en: en:potato origins:en: en:france en:Pro Montagna, ProMontagna, Coop Pro Montagna @@ -18546,6 +18564,7 @@ origins:en: en:france < fr:Fruits et Légumes de France fr:Fruits de France, fruits français +ingredients:en: en:fruit origins:en: en:france < fr:Fruits et Légumes de France @@ -18586,6 +18605,7 @@ label_categories:en: en:Quality, en:Environment wikidata:en: Q3214309 fr:Maïs de France +ingredients:en: en:corn origins:en: en:france < fr:Médaille d'argent du Concours de Bordeaux @@ -18803,6 +18823,10 @@ label_categories:en: en:Fishing method fr:Pêché au toc label_categories:en: en:Fishing method +fr:Pêche française +ingredients:en: en:fish +origins:en: en:france + fr:PIL, Produit de l'Industrie Locale fr:Premier cru @@ -18912,6 +18936,7 @@ fr:Tidy man wikidata:en: Q3528198 fr:Tomates de France +ingredients:en: en:tomato origins:en: en:france fr:Tréfilage au bronze, tréfilé au bronze diff --git a/taxonomies/labels.txt b/taxonomies/labels.txt index e2bd91ab96f08..133ba9b4f1864 100644 --- a/taxonomies/labels.txt +++ b/taxonomies/labels.txt @@ -2,12 +2,19 @@ # For labels that indicate the absence of something, in English, always make "No something" the first entry, and make "Something-free", "Without something" synonyms +# Supported and used properties: + +# When a label has both an "ingredients:en:" property + an "origins:en:" property (e.g. French eggs), +# the information is used to populate the origins of the ingredient when doing ingredients analysis. +# Note: ingredients:en: should list only 1 ingredient, while origins:en: can list multiple origins. +# Supporting multiple ingredients is doable but would require code changes. + stopwords:en:ingredients, agricultural, from, with, of, by, the, verified, certified, approved, 100%, pure, guaranty, guaranteed, product guaranteed, product certified, standard, during, period, phase, label, mark, logo stopwords:da:fra, kontrolleret, dyrkning, dyrket, dyrkede, råvarer, landbrug, certificeret stopwords:de:Zutaten, von, mit, enthält, kontrolliert, zertifiziert, geprüft, 100%, rein, Produkt, Garantie, garantiert, Standard, während stopwords:es:por, la, las, los, el, apto, para, verificado, certificado, aprovado, controlado, registrado, 100%, garantía, garantizado, garantizada, estándar stopwords:fi:ainesosat, vahvistettu, sertifioitu, hyväksytty, 100%, puhdas, tuote, standardi, valvottu, valvottua, tuotettu -stopwords:fr:ingrédient, ingrédients, agricole, agricoles, issu, issue, issus, issues, du, de, l', le, la, convient, peut convenir, au, aux, régime, régimes, conforme, validé, vérifié, certifié, par, un, une, approuvé, 100%, pur, garantie, garanti, produit garanti, produit certifié, sas, en, durant, pendant, phase, période, dès, label, marque, logo, produit issu, produits issus, tous, les, sont, est, issus de la production +stopwords:fr:ingrédient, ingrédients, agricole, agricoles, issu, issue, issus, issues, d', du, de, l', le, la, convient, peut convenir, au, aux, régime, régimes, conforme, validé, vérifié, certifié, par, un, une, approuvé, 100%, pur, garantie, garanti, produit garanti, produit certifié, sas, en, durant, pendant, phase, période, dès, label, marque, logo, produit issu, produits issus, tous, les, sont, est, issus de la production stopwords:hu:összetevők, mezőgazdaság, ellenőrzött, tanúsított, 100%, természetes, termék, garantált stopwords:ru:ингредиенты, из, с, проверено, сертифицировано, подтверждено, 100%, чистый, продукт, гарантия, гарантировано, стандарт stopwords:sv:certifierad, ingrediens, ingredienser, godkänd, råvara, enlighet, med, kriterierna, produkt, från, kontrollerat, kontrollerade, jordbruk, lantbruk, odling, odlingar @@ -212,6 +219,7 @@ fi:ranskalaisia munia, munien alkuperä Ranska fr:Oeufs de France, Oeufs français he:ביצים צרפתיות, ביצים מצרפת it:Uova francesi, Uova dalla Francia +ingredients:en: en:egg origins:en: en:france