<<

NAME

ProductOpener::Nutrition - functions related to nutrition facts of food products

DESCRIPTION

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.

..

FUNCTIONS

add_computed_values_to_nutrient_sets ($input_sets_ref)

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.

Arguments

$input_sets_ref

Reference to array of nutrient sets

generate_nutrient_aggregated_set

Generates the aggregated nutrient set for a product from its input sets and stores it in the product hash.

Arguments

$product_ref

Reference to the product hash

Return values

None

generate_nutrient_aggregated_set_from_sets

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.

Arguments

$input_sets_ref

Array of nutrients sets used to generate the aggregated set

Return values

The generated aggregated nutrient set

sort_sets_by_priority

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.

Arguments

$input_sets_ref

Reference to array of unsorted input nutrient sets

get_non_estimated_nutrient_per_100g_or_100ml_for_preparation ($product_ref, $preparation, $nid)

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.

Arguments

$product_ref

Reference to the product hash

$preparation

Preparation state to look for in the input sets

$nid of the nutrient to get

set_nutrient_values

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.

Arguments

$aggregated_nutrient_set_ref

The generated aggregated nutrient set.

@input_sets

The sorted array of nutrient set hashes used to generate the aggregated set.

convert_nutrient_to_standard_unit

Normalizes the unit of the nutrient value if necessary.

The normalized units are g, kJ or kcal based on the nutrient.

Arguments

$nutrient_ref

Hash of the nutrient to normalize

$nutrient_id

Name of the nutrient to normalize

convert_nutrient_to_100g

Converts the value of the amount of the nutrient based on the wanted per reference if necessary.

Arguments

$nutrient_ref

Hash of the nutrient set with the value to convert

$original_per_quantity

Current per amount of the nutrient

$original_per_unit

Current per unit of the nutrient

$wanted_per_quantity

Wanted per amount of the nutrient

$wanted_per_unit

Wanted per unit of the nutrient

get_specific_nutrition_input_set ($product_ref, $source, $preparation, $per)

Returns the input set matching the given source, preparation and per values.

Arguments

$product_ref

Reference to the product hash

$source

Source of the input set to find

$preparation

Preparation state of the input set to find

$per

Per reference of the input set to find

Return values

The input set hash reference if found, undef otherwise

set_per_quantity_and_unit($product_ref, $input_set_ref)

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".

Arguments

$product_ref - product

$input_set_ref - nutrient input set

It is modified to add per_quantity and per_unit.

get_nutrition_input_sets_in_a_hash ($product_ref)

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.

Arguments

$product_ref

Reference to the product hash

Return values

The hash reference of input sets

convert_nutrition_input_sets_hash_to_array ($input_sets_hash_ref, $product_ref)

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()

Arguments

$input_sets_hash_ref

Reference to hash of input sets

$product_ref

Used to get the serving size (quantity + unit) if needed for input sets with per = "serving"

Return values

Reference to array of input sets

get_source_for_site_and_org ( $org_id = undef )

Returns the default source of nutrition data for the current site and organization.

Arguments

$org_id

Organization id

Return values

- "packaging" for the public platform - "manufacturer" for the pro platform

get_preparations_for_product_type

Returns the list of valid preparation states for a given product type.

Arguments

$product_type

Type of the product (food, petfood, etc)

Return values

List of valid preparation states for the given product type

get_pers_for_product_type

Returns the list of valid per quantities for a given product type.

Arguments

$product_type

Type of the product (food, petfood, etc)

Return values

List of valid per references for the given product type

get_unit_options_for_nutrient ($nid)

Returns the list of valid unit options for a given nutrient.

Arguments

$nid

Nutrient id

Return values

Reference to an array of valid unit options for the given nutrient

normalize_nutrient_value_string_and_modifier ( $value_ref, $modifier_ref )

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.

Arguments

string value reference $value_ref

Input and output string value reference. The value will be normalized.

modifier reference $modifier_ref

Input and output modifier reference. The value will be normalized if it is defined.

Possible return values

value

- 0 if the input value indicates traces - Number (as a string) - undef for 'NaN' (not a number, sometimes sent by broken API clients)

modifier

<, >, ≤, ≥, ~ 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_nutrient_modifier_value_string_and_unit ($input_sets_hash_ref, $source, $preparation, $per, $nid, $modifier, $value_string, $unit)

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.

Parameters

$input_sets_hash_ref

Reference to the hash of input sets, as returned by get_nutrition_input_sets_in_a_hash

$source

Source of the nutrition data: e.g. "packaging", "manufacturer"

$preparation

"as_sold" or "prepared"

$per

"100g", "100ml", "serving", "1kg" (for pet food)

$nid

Nutrient id

value_string

unit

assign_nutrition_values_from_old_request_parameters ( $request_ref, $product_ref, $nutrient_table, $source )

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.

Parameters

$request_ref

Reference to the request parameters hash

$product_ref

Reference to the product hash where the nutrition data will be stored.

$nutrient_table

The nutriment table to use. It should be one of the keys of %nutrients_tables in Config.pm

$source

The source of the nutrition data. e.g. "packaging" or "manufacturer"

assign_nutrition_values_from_request_parameters ( $request_ref, $product_ref, $nutrient_table, $source )

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.

Parameters

$request_ref

Reference to the request object

$product_ref

Reference to the product hash where the nutrition data will be stored.

$nutrient_table

The nutriment table to use. It should be one of the keys of %nutrients_tables in Config.pm

$source

The source of the nutrition data. e.g. "packaging" or "manufacturer"

assign_nutrition_values_from_imported_csv_product ( $imported_csv_product_ref, $product_ref, $nutrient_table )

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.

Parameters

$imported_csv_product_ref

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

$product_ref

Reference to the product hash where the nutrition data will be stored.

$nutrient_table

The nutriment table to use. It should be one of the keys of %nutrients_tables in Config.pm

$source

The source of the nutrition data. e.g. "packaging" or "manufacturer"

import_nutrients_old_fields($args_ref, $imported_product_ref, $product_ref, $stats_ref, $modified_ref, $modified_fields_ref, $differing_ref, $differing_fields_ref, $nutrients_edited_ref, $time)

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.

assign_nutrition_values_from_request_object ( $request_ref, $product_ref )

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.

Parameters

$request_ref

Reference to the request object

$product_ref

Reference to the product hash where the nutrition data will be stored.

compute_estimated_nutrients ( $product_ref )

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.

remove_empty_nutrient_values_and_set_unspecified_nutrients ($input_set_ref)

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.

Arguments

$input_set_ref

Reference to the input set hash

add_nutrition_fields_from_product_to_populated_fields($product_ref, \%populated_fields, $sort_key)

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.

Arguments

$product_ref

Reference to the product hash

\%populated_fields

Reference to the hash of populated fields

$sort_key

A string used to sort the fields in the CSV file. The nutrition fields sort keys will be prefixed by this sort key.

filter_out_nutrients_not_in_taxonomy ($product_ref)

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.

Example input data

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,

convert_sodium_to_salt ( $sodium_value )

Converts a sodium value to its equivalent salt value using the EU standard conversion factor (2.5).

convert_salt_to_sodium ( $salt_value )

Converts a salt value to its equivalent sodium value using the EU standard conversion factor (2.5).

compute_energy_from_nutrients_for_nutrients_set ( $nutrients_ref, $unit )

Computes the energy from other nutrients for a given input set.

Parameters

$nutrients_ref: reference to the nutrients hash.

$unit: unit to use for energy computation ("kJ" or "kcal").

Returns

The computed energy value.

The value is also stored in energy nutrients hashes as "value_computed".

has_non_estimated_nutrition_data ( $product_ref )

Checks if the product has at least one nutrient with a non-estimated value

Arguments

$product_ref

Reference to the product hash

get_nutrition_data_as_key_values_pairs ( $product_ref )

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.

Arguments

$product_ref

Reference to the product hash

Returns

A reference to a hash of key-value pairs representing the nutrition data of the product.

has_no_nutrition_data_on_packaging ( $product_ref )

Checks if the product has the no_nutrition_data_on_packaging flag set to true.

Arguments

$product_ref

Reference to the product hash

remove_empty_nutrition_data ( $product_ref )

Remove the empty nutrition data (empty input_sets and/or aggregated_set), and the nutrition hash if it is empty.

get_nutrient_from_nutrient_set_in_default_unit ( $nutrients_ref, $nid )

Get the value of a nutrient from a nutrients set, converted to the default unit for that nutrient.

Parameters

$nutrients_ref: reference to the nutrients hash.

$nid: nutrient id.

Returns

The value of the nutrient in the default unit, or undef if not defined.

default_unit_for_nid ( $nid)

Return the default unit that we convert everything to internally (e.g. in the aggregated set)

Parameters

$nid: String

Return values

Default value for that certain unit

add_misc_tags_for_input_nutrition_data_pers ( $product_ref )

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;

<<