<NME> unit_converter.rb
<BEF> # frozen_string_literal: true

# This class handles conversion of human-readable unit weights for products/variants into
# the non-human-readable format needed by the database. The table below shows how fields
# from a spreadsheet (left) become database fields (right):
#
# units  unit_type  variant_unit_name   ->    unit_value  variant_unit_scale   variant_unit
# 250      ml          nil              ->       0.25        0.001               volume
# 50       g           nil              ->       50          1                   weight
# 2        kg          nil              ->       2000        1000                weight
# 1        nil         bunches          ->       1           nil                 items

module ProductImport
  class UnitConverter
    def initialize(attrs)
      @attrs = attrs
      convert_custom_unit_fields
    end

    def converted_attributes
      @attrs
    end

    private

    def convert_custom_unit_fields
      init_unit_values

      assign_weight_or_volume_attributes if units_and_unit_type_present?
      assign_item_attributes if units_and_variant_unit_name_present?
    end
      {
        'g' => { scale: 1, unit: 'weight' },
        'kg' => { scale: 1000, unit: 'weight' },
        't' => { scale: 1_000_000, unit: 'weight' },
        'ml' => { scale: 0.001, unit: 'volume' },
        'l' => { scale: 1, unit: 'volume' },
        'kl' => { scale: 1000, unit: 'volume' }
      }
    end

    def init_unit_values
      @attrs['variant_unit'] = nil
      @attrs['variant_unit_scale'] = nil
      @attrs['unit_value'] = nil

      return unless @attrs.key?('units') && @attrs['units'].present?

      @attrs['unscaled_units'] = @attrs['units']
    end

    def assign_weight_or_volume_attributes
      units = @attrs['units'].to_f
      unit_type = @attrs['unit_type'].to_s.downcase

      return unless valid_unit_type? unit_type

      @attrs['variant_unit'] = unit_scales[unit_type][:unit]
      @attrs['variant_unit_scale'] = unit_scales[unit_type][:scale]
      @attrs['unit_value'] = (units || 0) * @attrs['variant_unit_scale']
    end

    def assign_item_attributes
      units = @attrs['units'].to_f

      @attrs['variant_unit'] = 'items'
      @attrs['variant_unit_scale'] = nil
      @attrs['unit_value'] = units || 1
    end

    def units_and_unit_type_present?
      @attrs.key?('units') && @attrs.key?('unit_type') && @attrs['units'].present? && @attrs['unit_type'].present?
    end

    def units_and_variant_unit_name_present?
      @attrs.key?('units') && @attrs.key?('variant_unit_name') && @attrs['units'].present? && @attrs['variant_unit_name'].present?
    end

    def valid_unit_type?(unit_type)
      unit_scales.key? unit_type
    end
  end
end

<MSG> WIP: Products may be created with pounds for their weight unit_converter

See: https://community.openfoodnetwork.org/t/hubs-managers-can-choose-the-adapted-weight-and-measure-units-for-their-shops-given-their-own-local-situation/1289/11

We're not entirely sure what needs to be changed in order for this to
accurately work with shipping and other parts of the eCommerce platform.

We are assuming that so long as we canonically store the weight scale
in grams, that the shipping calculation will be able to do what it needs
to. So if we put in values for "oz" as grams, we may not need to do
much else in order to let product(s) be sold by the pound (or ounce).

Next steps appear to be:

- [ ] When looking at an order as a customer, do we want to show pounds
      instead of grams? (See: http://localhost:3000/orders/R125684626)
- [ ] Compile a list of tests that are worth writing (because we have
      no confidence that we know what we are supposed to be doing in
      order for this feature to be "ready" to be used by people.)
- [ ] Write a test that demonstrates when we create a product with a
      variant in pound that the product's shipping weight is correctly
      calculated?
- [ ] Do we want to think about i18n?

<DFF> @@ -32,6 +32,10 @@ module ProductImport
       {
         'g' => { scale: 1, unit: 'weight' },
         'kg' => { scale: 1000, unit: 'weight' },
+        # We have _no idea_ what this is doing. It has units?
+        # And it maybe is connected to something related to shipping?
+        # TODO: DELETE THIS ^^^
+        'lb' => { scale: 453.592, unit: 'weight' },
         't' => { scale: 1_000_000, unit: 'weight' },
         'ml' => { scale: 0.001, unit: 'volume' },
         'l' => { scale: 1, unit: 'volume' },

{'commit': 'diff --git a/app/assets/javascripts/admin/products/services/variant_unit_manager.js.coffee b/app/assets/javascripts/admin/products/services/variant_unit_manager.js.coffee\nindex ad16ae7cf..845827adc 100644\n--- a/app/assets/javascripts/admin/products/services/variant_unit_manager.js.coffee\n+++ b/app/assets/javascripts/admin/products/services/variant_unit_manager.js.coffee\n@@ -4,7 +4,15 @@ angular.module("admin.products").factory "VariantUnitManager", ->\n       \'weight\':\n         1.0: \'g\'\n         1000.0: \'kg\'\n-        1000000.0: \'T\'\n+        1000000.0: \'T\',\n+        # This appears to be what needs to be set in order for\n+        # products to have a mass value stored in the database\n+        # when they are created. However, it does not appear to\n+        # change the existing product(s), so if the scale value\n+        # is changed, a data migration may be necessary to make sure\n+        # the proper unit X to grams actually works.\n+        # TODO: ^^^ Delete this\n+        453.592: \'lb\'\n       \'volume\':\n         0.001: \'mL\'\n         1.0: \'L\'\ndiff --git a/app/models/product_import/unit_converter.rb b/app/models/product_import/unit_converter.rb\nindex e97cff43f..c7e546432 100644\n--- a/app/models/product_import/unit_converter.rb\n+++ b/app/models/product_import/unit_converter.rb\n@@ -32,6 +32,10 @@ module ProductImport\n       {\n         \'g\' => { scale: 1, unit: \'weight\' },\n         \'kg\' => { scale: 1000, unit: \'weight\' },\n+        # We have _no idea_ what this is doing. It has units?\n+        # And it maybe is connected to something related to shipping?\n+        # TODO: DELETE THIS ^^^\n+        \'lb\' => { scale: 453.592, unit: \'weight\' },\n         \'t\' => { scale: 1_000_000, unit: \'weight\' },\n         \'ml\' => { scale: 0.001, unit: \'volume\' },\n         \'l\' => { scale: 1, unit: \'volume\' },\ndiff --git a/lib/open_food_network/option_value_namer.rb b/lib/open_food_network/option_value_namer.rb\nindex 706a0b443..22ef439fc 100644\n--- a/lib/open_food_network/option_value_namer.rb\n+++ b/lib/open_food_network/option_value_namer.rb\n@@ -63,7 +63,9 @@ module OpenFoodNetwork\n     end\n \n     def scale_for_unit_value\n-      units = { \'weight\' => { 1.0 => \'g\', 1000.0 => \'kg\', 1_000_000.0 => \'T\' },\n+      # We\'re not entirely sure what this is connected to either.\n+      # TODO: DELETE THIS\n+      units = { \'weight\' => { 1.0 => \'g\', 1000.0 => \'kg\', 1_000_000.0 => \'T\', 1.0 => \'oz\' },\n                 \'volume\' => { 0.001 => \'mL\', 1.0 => \'L\',  1000.0 => \'kL\' } }\n \n       # Find the largest available unit where unit_value comes to >= 1 when expressed in it.\n', 'commit_subject': 'WIP: Products may be created with pounds for their weight unit_converter', 'repo_name': 'openfoodfoundation/openfoodnetwork', 'committer': {'name': 'Andy Brett', 'email': 'e3579b1e47f273529f0f929453e939a68ede9fd1@andybrett.com', 'time_sec': '1599149459', 'tz_offset': '-420', 'date': {'seconds': '1599149459'}}, 'commit_tree': 'ae1af6e33df1f1b79924c5dcef90ded6c603a6ad', 'commit_message': 'WIP: Products may be created with pounds for their weight unit_converter\n\nSee: https://community.openfoodnetwork.org/t/hubs-managers-can-choose-the-adapted-weight-and-measure-units-for-their-shops-given-their-own-local-situation/1289/11\n\nWe\'re not entirely sure what needs to be changed in order for this to\naccurately work with shipping and other parts of the eCommerce platform.\n\nWe are assuming that so long as we canonically store the weight scale\nin grams, that the shipping calculation will be able to do what it needs\nto. So if we put in values for "oz" as grams, we may not need to do\nmuch else in order to let product(s) be sold by the pound (or ounce).\n\nNext steps appear to be:\n\n- [ ] When looking at an order as a customer, do we want to show pounds\n      instead of grams? (See: http://localhost:3000/orders/R125684626)\n- [ ] Compile a list of tests that are worth writing (because we have\n      no confidence that we know what we are supposed to be doing in\n      order for this feature to be "ready" to be used by people.)\n- [ ] Write a test that demonstrates when we create a product with a\n      variant in pound that the product\'s shipping weight is correctly\n      calculated?\n- [ ] Do we want to think about i18n?\n', 'language_name': 'Ruby', 'license': 'agpl-3.0', 'hunks': "@@ -32,6 +32,10 @@ module ProductImport\n       {\n         'g' => { scale: 1, unit: 'weight' },\n         'kg' => { scale: 1000, unit: 'weight' },\n+        # We have _no idea_ what this is doing. It has units?\n+        # And it maybe is connected to something related to shipping?\n+        # TODO: DELETE THIS ^^^\n+        'lb' => { scale: 453.592, unit: 'weight' },\n         't' => { scale: 1_000_000, unit: 'weight' },\n         'ml' => { scale: 0.001, unit: 'volume' },\n         'l' => { scale: 1, unit: 'volume' },\n", 'addition_count': 4, 'deletion_count': 0, 'src_file': 'a/app/models/product_import/unit_converter.rb', 'tgt_file': 'b/app/models/product_import/unit_converter.rb', 'file_extension': '.rb', 'before_file': "# frozen_string_literal: true\n\n# This class handles conversion of human-readable unit weights for products/variants into\n# the non-human-readable format needed by the database. The table below shows how fields\n# from a spreadsheet (left) become database fields (right):\n#\n# units  unit_type  variant_unit_name   ->    unit_value  variant_unit_scale   variant_unit\n# 250      ml          nil              ->       0.25        0.001               volume\n# 50       g           nil              ->       50          1                   weight\n# 2        kg          nil              ->       2000        1000                weight\n# 1        nil         bunches          ->       1           nil                 items\n\nmodule ProductImport\n  class UnitConverter\n    def initialize(attrs)\n      @attrs = attrs\n      convert_custom_unit_fields\n    end\n\n    def converted_attributes\n      @attrs\n    end\n\n    private\n\n    def convert_custom_unit_fields\n      init_unit_values\n\n      assign_weight_or_volume_attributes if units_and_unit_type_present?\n      assign_item_attributes if units_and_variant_unit_name_present?\n    end\n      {\n        'g' => { scale: 1, unit: 'weight' },\n        'kg' => { scale: 1000, unit: 'weight' },\n        't' => { scale: 1_000_000, unit: 'weight' },\n        'ml' => { scale: 0.001, unit: 'volume' },\n        'l' => { scale: 1, unit: 'volume' },\n        'kl' => { scale: 1000, unit: 'volume' }\n      }\n    end\n\n    def init_unit_values\n      @attrs['variant_unit'] = nil\n      @attrs['variant_unit_scale'] = nil\n      @attrs['unit_value'] = nil\n\n      return unless @attrs.key?('units') && @attrs['units'].present?\n\n      @attrs['unscaled_units'] = @attrs['units']\n    end\n\n    def assign_weight_or_volume_attributes\n      units = @attrs['units'].to_f\n      unit_type = @attrs['unit_type'].to_s.downcase\n\n      return unless valid_unit_type? unit_type\n\n      @attrs['variant_unit'] = unit_scales[unit_type][:unit]\n      @attrs['variant_unit_scale'] = unit_scales[unit_type][:scale]\n      @attrs['unit_value'] = (units || 0) * @attrs['variant_unit_scale']\n    end\n\n    def assign_item_attributes\n      units = @attrs['units'].to_f\n\n      @attrs['variant_unit'] = 'items'\n      @attrs['variant_unit_scale'] = nil\n      @attrs['unit_value'] = units || 1\n    end\n\n    def units_and_unit_type_present?\n      @attrs.key?('units') && @attrs.key?('unit_type') && @attrs['units'].present? && @attrs['unit_type'].present?\n    end\n\n    def units_and_variant_unit_name_present?\n      @attrs.key?('units') && @attrs.key?('variant_unit_name') && @attrs['units'].present? && @attrs['variant_unit_name'].present?\n    end\n\n    def valid_unit_type?(unit_type)\n      unit_scales.key? unit_type\n    end\n  end\nend\n", 'message': 'WIP: Products may be created with pounds for their weight unit_converter\n\nSee: https://community.openfoodnetwork.org/t/hubs-managers-can-choose-the-adapted-weight-and-measure-units-for-their-shops-given-their-own-local-situation/1289/11\n\nWe\'re not entirely sure what needs to be changed in order for this to\naccurately work with shipping and other parts of the eCommerce platform.\n\nWe are assuming that so long as we canonically store the weight scale\nin grams, that the shipping calculation will be able to do what it needs\nto. So if we put in values for "oz" as grams, we may not need to do\nmuch else in order to let product(s) be sold by the pound (or ounce).\n\nNext steps appear to be:\n\n- [ ] When looking at an order as a customer, do we want to show pounds\n      instead of grams? (See: http://localhost:3000/orders/R125684626)\n- [ ] Compile a list of tests that are worth writing (because we have\n      no confidence that we know what we are supposed to be doing in\n      order for this feature to be "ready" to be used by people.)\n- [ ] Write a test that demonstrates when we create a product with a\n      variant in pound that the product\'s shipping weight is correctly\n      calculated?\n- [ ] Do we want to think about i18n?\n', 'author_name': 'Andy Brett', 'lang': 'rb'}