ProductOpener::Nutrition - functions related to nutrition facts of food products
ProductOpener::Nutrition contains functions specific to food products,
in particular related to the new schema of nutrition facts.
This module provides functions It does not contain functions related to ingredients which are in the ProductOpener::Ingredients module.
..
Adds computed values to each nutrient set in the given array of nutrient sets:
- energy is computed from macronutrients if enough macronutrients are available - salt and sodium are computed from each other
Values are stored in the value_computed field of each nutrient.
Reference to array of nutrient sets
Generates the aggregated nutrient set for a product from its input sets and stores it in the product hash.
Reference to the product hash
None
Generates and returns a hash reference of the aggregated nutrient set from the given list of nutrient sets.
The generated set is a combined set of nutrients with the preferred sources, per references and preparation states and with normalized units.
Array of nutrients sets used to generate the aggregated set
The generated aggregated nutrient set
Sorts hashes of nutrient sets in a given array based on a custom priority. The array is sorted in place and also returned.
The priority is based on the preparation states, the sources, and the per references present in the nutrient sets.
We want the preparation state first, as if we have prepared data, then we should use prepared data to compute Nutri-Score etc. Then we want the source, as some sources are more reliable than others (e.g. estimates from ingredients should be last) Then we want the per reference, as we will convert to 100g or 100ml if possible, so we want to prefer sets that already have 100g or 100ml as per reference.
Reference to array of unsorted input nutrient sets
Gets the value of a nutrient from the first non-estimated input set with per = 100g or 100ml. We take the first value defined in the sorted input set, that we can convert to 100g or 100ml.
This function is needed to estimate the % of ingredients, in order to set the max for ingredients like salt and sugar.
Reference to the product hash
Preparation state to look for in the input sets
For each nutrient appearing in the nutrient sets array, sets its values in the aggregated set.
The units of the nutrients quantities are normalized (g, kJ or kcal).
Each nutrient is only added once. Its value is the one in the set with the highest priority.
If the preparation value in a set is different from the one in the aggregated set, the nutrient is not added to the aggregated set.
The generated aggregated nutrient set.
The sorted array of nutrient set hashes used to generate the aggregated set.
Normalizes the unit of the nutrient value if necessary.
The normalized units are g, kJ or kcal based on the nutrient.
Hash of the nutrient to normalize
Name of the nutrient to normalize
Converts the value of the amount of the nutrient based on the wanted per reference if necessary.
Hash of the nutrient set with the value to convert
Current per amount of the nutrient
Current per unit of the nutrient
Wanted per amount of the nutrient
Wanted per unit of the nutrient
Returns the input set matching the given source, preparation and per values.
Reference to the product hash
Source of the input set to find
Preparation state of the input set to find
Per reference of the input set to find
The input set hash reference if found, undef otherwise
Fill the per_quantity and per_unit field of nutrients input set, based upon the "per" value of the nutrient input set, or product serving_quantity and unit for "serving".
It is modified to add per_quantity and per_unit.
Returns the input sets of a product in a hash reference for easier access, so that we can use $input_sets_hash_ref->{$source}{$preparation}{$per} to get a specific input set.
Reference to the product hash
The hash reference of input sets
Converts a hash reference of input sets back to an array reference, which is the format we store in the product structure
Input sets are normalized: - nutrients with undefined or empty values are removed - nutrient with a modifier "-" are removed and added to the unspecified nutrients array - input sets with no nutrients are removed - the source, preparation and per values from the input sets hash keys are set in the input set
The nutrients sets are sorted with sort_sets_by_priority()
Reference to hash of input sets
Used to get the serving size (quantity + unit) if needed for input sets with per = "serving"
Reference to array of input sets
Returns the default source of nutrition data for the current site and organization.
Organization id
- "packaging" for the public platform - "manufacturer" for the pro platform
Returns the list of valid preparation states for a given product type.
Type of the product (food, petfood, etc)
List of valid preparation states for the given product type
Returns the list of valid per quantities for a given product type.
Type of the product (food, petfood, etc)
List of valid per references for the given product type
Returns the list of valid unit options for a given nutrient.
Nutrient id
Reference to an array of valid unit options for the given nutrient
Each nutrient value is entered as a string (by users on the product edit form, or through the API). The string value may not always be numeric (e.g. it can include a < sign).
This function normalizes the string value to remove signs, and stores extra information in the "modifier" field.
Modifiers may also be inputted directly (e.g. through the API), and are normalized as well.
Input and output string value reference. The value will be normalized.
Input and output modifier reference. The value will be normalized if it is defined.
- 0 if the input value indicates traces - Number (as a string) - undef for 'NaN' (not a number, sometimes sent by broken API clients)
<, >, ≤, ≥, ~ character sign, for lesser, greater, lesser or equal, greater or equal, and about - (minus sign) character when the input value is - (or other dashes) : indicates that the value is not present on the package
Assign a value with a unit and an optional modifier (< or ~) to a nutrient in the nutriments structure.
If a modifier, value_string or unit is undef or empty, the corresponding field is set to undef.
Reference to the hash of input sets, as returned by get_nutrition_input_sets_in_a_hash
Source of the nutrition data: e.g. "packaging", "manufacturer"
"as_sold" or "prepared"
"100g", "100ml", "serving", "1kg" (for pet food)
Nutrient id
This function provides backward compatibility for apps that use product edit API v2 (/cgi/product_jqm_multingual.pl) before the introduction of the new nutrition data schema.
It reads the old nutrition data parameters from the request, and assigns them to the new product nutrition structure.
Reference to the request parameters hash
Reference to the product hash where the nutrition data will be stored.
The nutriment table to use. It should be one of the keys of %nutrients_tables in Config.pm
The source of the nutrition data. e.g. "packaging" or "manufacturer"
This function is used by the web product edit form and apps that use product edit API v2 (/cgi/product_jqm_multingual.pl) after the introduction of the new nutrition data schema.
It reads the new nutrition data parameters from the request, and assigns them to the new product nutrition structure.
Reference to the request object
Reference to the product hash where the nutrition data will be stored.
The nutriment table to use. It should be one of the keys of %nutrients_tables in Config.pm
The source of the nutrition data. e.g. "packaging" or "manufacturer"
This function is used by Import.pm to import new nutrition data from an imported product (though a CSV file) to an existing product.
It reads the new nutrition data parameters from the imported product, and assigns them to the new product nutrition structure.
Note: the serving_size fields need to be imported first, as we need it to set the per_quantity and per_unit fields of the "serving" input sets.
Note: a source is not specified as argument to this function, as it should be set in the field names.
Reference to the imported product hash where the nutrition data will be read.
All the fields in the imported product are at the root level, e.g. nutrition.input_sets.as_sold.100g.nutrients.energy.value_string
Reference to the product hash where the nutrition data will be stored.
The nutriment table to use. It should be one of the keys of %nutrients_tables in Config.pm
The source of the nutrition data. e.g. "packaging" or "manufacturer"
Import nutrient values from old style fields like fat_100g_value, fat_100g_unit, fat_prepared_100g_value, etc.
We consider the source to be "packaging" on the public platform, and "manufacturer" on the producers platform
Note: the serving_size fields need to be imported first, as we need it to set the per_quantity and per_unit fields of the "serving" input sets.
This function is used by the product edit API v3 (/api/v3/product) to write the nutrition data. Nutrition data is passed in nutrition.input_sets as an array of input sets.
It reads the nutrition data from the request, and assigns them to the new product nutrition structure.
Reference to the request object
Reference to the product hash where the nutrition data will be stored.
Compute estimated nutrients from ingredients.
If we have a high enough confidence (95% of the ingredients (by quantity) have a known nutrient profile), we store the result in an nutrient input set with the source "estimate".
Otherwise we remove the estimated nutrients input set if it exists.
Removes nutrients with empty values from an input set.
If a nutrient has a modifier equal to "-", it means no value is specified on the packaging.
The nutrient is removed from the input set and added to the unspecified_nutrients array.
Reference to the input set hash
This function is used by Export.pm to generate the list of populated nutrition fields for a product. Export.pm can then create a CSV file with columns that have data for at least one product.
If we have a value for a nutrient in an input set of the product, we add the corresponding field to the populated fields hash, so that it can be output in the CSV file.
e.g. for an input set like:
input_sets => [ { preparation => "as_sold", per => "serving", per_quantity => "1", per_unit => "l", source => "packaging", nutrients => { "sodium" => { value_string => "0.25", value => 0.25, unit => "g", }, "sugars" => { value_string => "2.0", value => 2, unit => "g", } } }, { preparation => "as_sold", per => "serving", per_quantity => "50", per_unit => "ml", source => "manufacturer", nutrients => { "sugars" => { value_string => "0.063", value => 0.063, unit => "g", } } } ]
We generate those keys and values in the populated fields hash:
{ "nutrition.input_sets.manufacturer.as_sold.serving.nutrients.sugars.unit" : "nutrition_01-manufacturer_as_sold_serving_2-nutrients_043-sugars_unit", "nutrition.input_sets.manufacturer.as_sold.serving.nutrients.sugars.value" : "nutrition_01-manufacturer_as_sold_serving_2-nutrients_043-sugars_value", "nutrition.input_sets.manufacturer.as_sold.serving.nutrients.sugars.value_string" : "nutrition_01-manufacturer_as_sold_serving_2-nutrients_043-sugars_value_string", "nutrition.input_sets.manufacturer.as_sold.serving.per_quantity" : "nutrition_01-manufacturer_as_sold_serving_0-root_per_quantity", "nutrition.input_sets.manufacturer.as_sold.serving.per_unit" : "nutrition_01-manufacturer_as_sold_serving_0-root_per_unit", "nutrition.input_sets.packaging.as_sold.serving.nutrients.sodium.unit" : "nutrition_01-packaging_as_sold_serving_2-nutrients_068-sodium_unit", "nutrition.input_sets.packaging.as_sold.serving.nutrients.sodium.value" : "nutrition_01-packaging_as_sold_serving_2-nutrients_068-sodium_value", "nutrition.input_sets.packaging.as_sold.serving.nutrients.sodium.value_string" : "nutrition_01-packaging_as_sold_serving_2-nutrients_068-sodium_value_string", "nutrition.input_sets.packaging.as_sold.serving.nutrients.sugars.unit" : "nutrition_01-packaging_as_sold_serving_2-nutrients_043-sugars_unit", "nutrition.input_sets.packaging.as_sold.serving.nutrients.sugars.value" : "nutrition_01-packaging_as_sold_serving_2-nutrients_043-sugars_value", "nutrition.input_sets.packaging.as_sold.serving.nutrients.sugars.value_string" : "nutrition_01-packaging_as_sold_serving_2-nutrients_043-sugars_value_string", "nutrition.input_sets.packaging.as_sold.serving.per_quantity" : "nutrition_01-packaging_as_sold_serving_0-root_per_quantity", "nutrition.input_sets.packaging.as_sold.serving.per_unit" : "nutrition_01-packaging_as_sold_serving_0-root_per_unit" }
The key is the CSV field name, and the value is a sort key used to sort the fields in the CSV file.
Reference to the product hash
Reference to the hash of populated fields
A string used to sort the fields in the CSV file. The nutrition fields sort keys will be prefixed by this sort key.
In the old nutrition facts schema (2025 and before), we authorized users to add any nutrient they wanted, even if they did not exist in the taxonomy. In the new nutrition facts schema, we only authorize nutrients that exist in the taxonomy.
This function tries to map unknown nutrients to known nutrients in the taxonomy (as the taxonomy is evolving, some nutrients that were unknown before may now exist in the taxonomy). It then filters out nutrients that do not exist in the taxonomy and that could not be mapped to known nutrients.
Removes nutrients from the product's nutriments hash that are not present in the taxonomy.
The following are examples of unknown nutrients that may be present in the nutriments hash:
# unknown nutrient prefixed with language 'fr-nitrate' => 0.38, 'fr-nitrate_100g' => 0.38, 'fr-nitrate_label' => "Nitrate", 'fr-nitrate_serving' => 0.0038, 'fr-nitrate_unit' => "g", 'fr-nitrate_value' => 0.38, # unknown nutrient not prefixed with language (old fields) 'sulfat' => 0.0141, 'sulfat_100g' => 0.0141, 'sulfat_label' => "Sulfat", 'sulfat_serving' => 0.141, 'sulfat_unit' => "mg", 'sulfat_value' => 14.1, # unknown nutrient that is not in the taxonomy 'en-some-unknown-nutrient' => 1.23, 'en-some-unknown-nutrient_100g' => 1.23, 'en-some-unknown-nutrient_label' => "Some unknown nutrient", 'en-some-unknown-nutrient_unit' => "g", 'en-some-unknown-nutrient_value' => 1.23,
Converts a sodium value to its equivalent salt value using the EU standard conversion factor (2.5).
Converts a salt value to its equivalent sodium value using the EU standard conversion factor (2.5).
Computes the energy from other nutrients for a given input set.
The computed energy value.
The value is also stored in energy nutrients hashes as "value_computed".
Checks if the product has at least one nutrient with a non-estimated value
Reference to the product hash
This function is used by ProductOpener::Product::complete_product_history_and_completeness()
It serialize the nutrition data into key-value pairs (flat hashmap) for easier comparison of product history entries.
Estimates are not returned.
Reference to the product hash
A reference to a hash of key-value pairs representing the nutrition data of the product.
Checks if the product has the no_nutrition_data_on_packaging flag set to true.
Reference to the product hash
Remove the empty nutrition data (empty input_sets and/or aggregated_set), and the nutrition hash if it is empty.
Get the value of a nutrient from a nutrients set, converted to the default unit for that nutrient.
The value of the nutrient in the default unit, or undef if not defined.
Return the default unit that we convert everything to internally (e.g. in the aggregated set)
$nid: String
Default value for that certain unit
Add misc tags for the different pers used in the input nutrition data, to make it easier to filter products based on the pers used in their nutrition data. (e.g. to find all ice creams with per 100ml)
Estimates are not included.
add_tag($product_ref, "misc", "en:nutrition-data-per-100g") if we have at least one input set with per 100g;