engines/dfc_provider/app/services/quantitative_value_builder.rb
# frozen_string_literal: true
# Describes the quantity contained in a product, for example:
#
# - 1 piece of apple, usually meaning the whole fruit
# - 2 litres of milk, for example in a big bottle or pouch
# - 750 grams of bread, for example a loaf
#
# The DFC also supports specific units like loafs and cans but we don't have
# standardised data within OFN to map to these types.
class QuantitativeValueBuilder < DfcBuilder
def self.quantity(variant)
DataFoodConsortium::Connector::QuantitativeValue.new(
unit: unit(variant.product.variant_unit),
value: variant.unit_value,
)
end
def self.unit(unit_name)
case unit_name
when "volume"
DfcLoader.connector.MEASURES.LITRE
when "weight"
DfcLoader.connector.MEASURES.GRAM
else
DfcLoader.connector.MEASURES.PIECE
end
end
def self.apply(quantity, product)
measure, unit_name, unit_scale = map_unit(quantity.unit)
product.variant_unit = measure
product.variant_unit_name = unit_name if measure == "items"
product.variant_unit_scale = unit_scale
product.unit_value = quantity.value.to_f * unit_scale
end
# Map DFC units to OFN fields:
#
# - variant_unit
# - variant_unit_name
# - variant_unit_scale
#
# Unimplemented measures
#
# The DFC knows lots of single piece measures like a tub. There are not
# listed here and automatically mapped to "item". The following is a list
# of measures we want or could implement.
#
# Length is not represented in the OFN:
#
# :CENTIMETRE,
# :DECIMETRE,
# :METRE,
# :KILOMETRE,
# :INCH,
#
# Other:
#
# :PERCENT,
#
# This method is quite long and may be shortened with new DFC features:
#
# * https://github.com/datafoodconsortium/taxonomies/issues/7
# * https://github.com/datafoodconsortium/connector-ruby/issues/18
#
# Until then, we can ignore Rubocop metrics, IMO.
def self.map_unit(unit) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
quantity_unit = DfcLoader.connector.MEASURES
# The unit name is only set for items. The name is implied for weight and
# volume and filled in by `WeightsAndMeasures`.
case unit
when quantity_unit.LITRE
["volume", nil, 1]
when quantity_unit.MILLILITRE
["volume", nil, 0.001]
when quantity_unit.CENTILITRE
["volume", nil, 0.01]
when quantity_unit.DECILITRE
["volume", nil, 0.1]
when quantity_unit.GALLON
["volume", nil, 4.54609]
when quantity_unit.MILLIGRAM
["weight", nil, 0.001]
when quantity_unit.GRAM
["weight", nil, 1]
when quantity_unit.KILOGRAM
["weight", nil, 1_000]
when quantity_unit.TONNE
["weight", nil, 1_000_000]
# Not part of the DFC yet:
# when quantity_unit.OUNCE
# ["weight", nil, 28.349523125]
when quantity_unit.POUNDMASS
["weight", nil, 453.59237]
when quantity_unit.PAIR
["items", "pair", 2]
when quantity_unit._4PACK
["items", "4 pack", 4]
when quantity_unit._6PACK
["items", "6 pack", 6]
when quantity_unit.HALFDOZEN
["items", "half dozen", 6]
when quantity_unit.DOZEN
["items", "dozen", 12]
else
# Labels may be provided one day:
# https://github.com/datafoodconsortium/connector-ruby/issues/18
unit_id = unit.try(:semanticId)&.split("#")&.last&.split(":")&.last
label = unit_id || "items"
["items", label, 1]
end
end
end