From 83beafc564a5cbd81390ff9d544184fb31cd1aef Mon Sep 17 00:00:00 2001 From: Gaetan Craig-Riou Date: Fri, 27 Oct 2023 15:47:38 +1100 Subject: [PATCH 01/24] Add missing translation on taxon admin form --- app/views/spree/admin/taxons/_form.html.haml | 12 ++++++------ config/locales/en.yml | 8 ++++++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/app/views/spree/admin/taxons/_form.html.haml b/app/views/spree/admin/taxons/_form.html.haml index a23a55be0fa..37143af832b 100644 --- a/app/views/spree/admin/taxons/_form.html.haml +++ b/app/views/spree/admin/taxons/_form.html.haml @@ -1,31 +1,31 @@ .row .alpha.five.columns = f.field_container :name do - = f.label :name, t(:name) + = f.label :name, t(".name") %span.required * %br/ = error_message_on :taxon, :name, class: 'fullwidth title' = text_field :taxon, :name, class: 'fullwidth' = f.field_container :permalink_part do - = f.label :permalink_part, t(:permalink) + = f.label :permalink_part, t(".permalink") %span.required * %br/ = @taxon.permalink.split("/")[0...-1].join("/") + "/" = text_field_tag :permalink_part, @permalink_part = f.field_container :meta_title do - = f.label :meta_title, t(:meta_title) + = f.label :meta_title, t(".meta_title") %br/ = f.text_field :meta_title, class: 'fullwidth', rows: 6 = f.field_container :meta_description do - = f.label :meta_description, t(:meta_description) + = f.label :meta_description, t(".meta_description") %br/ = f.text_field :meta_description, class: 'fullwidth', rows: 6 = f.field_container :meta_description do - = f.label :meta_keywords, t(:meta_keywords) + = f.label :meta_keywords, t(".meta_keywords") %br/ = f.text_field :meta_keywords, class: 'fullwidth', rows: 6 .omega.seven.columns = f.field_container :description do - = f.label :description, t(:description) + = f.label :description, t(".description") %br/ = f.text_area :description, class: 'fullwidth', rows: 6 diff --git a/config/locales/en.yml b/config/locales/en.yml index 4289cc584c1..d3bb4d29601 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -4479,6 +4479,14 @@ See the %{link} to find out more about %{sitename}'s features and to start using email: "Email" total: "Total" billing_address_name: "Name" + taxons: + form: + name: Name + permalink: Permalink + meta_title: Meta Title + meta_description: Meta Description + meta_keywords: Meta Keywords + description: Description general_settings: edit: legal_settings: "Legal Settings" From 652c7a563cdb9efaa77b10755abba047b83a32c4 Mon Sep 17 00:00:00 2001 From: Gaetan Craig-Riou Date: Fri, 27 Oct 2023 15:48:47 +1100 Subject: [PATCH 02/24] Add dfc_name field on Spree::Taxons This will let us map OFN product taxons to DFC product types --- app/controllers/spree/admin/taxons_controller.rb | 4 ++-- app/views/spree/admin/taxons/_form.html.haml | 4 ++++ config/locales/en.yml | 1 + db/migrate/20231027041224_add_dfc_name_to_spree_taxons.rb | 5 +++++ db/schema.rb | 1 + 5 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20231027041224_add_dfc_name_to_spree_taxons.rb diff --git a/app/controllers/spree/admin/taxons_controller.rb b/app/controllers/spree/admin/taxons_controller.rb index b5868661104..2cc60bbd7eb 100644 --- a/app/controllers/spree/admin/taxons_controller.rb +++ b/app/controllers/spree/admin/taxons_controller.rb @@ -117,8 +117,8 @@ def destroy def taxon_params params.require(:taxon).permit( - :name, :parent_id, :position, :icon, :description, :permalink, - :taxonomy_id, :meta_description, :meta_keywords, :meta_title + :name, :parent_id, :position, :icon, :description, :permalink, :taxonomy_id, + :meta_description, :meta_keywords, :meta_title, :dfc_name ) end end diff --git a/app/views/spree/admin/taxons/_form.html.haml b/app/views/spree/admin/taxons/_form.html.haml index 37143af832b..df23dde53ae 100644 --- a/app/views/spree/admin/taxons/_form.html.haml +++ b/app/views/spree/admin/taxons/_form.html.haml @@ -24,6 +24,10 @@ = f.label :meta_keywords, t(".meta_keywords") %br/ = f.text_field :meta_keywords, class: 'fullwidth', rows: 6 + = f.field_container :dfc_name do + = f.label :dfc_name, t(".dfc_name") + %br/ + = f.text_field :dfc_name, class: 'fullwidth', rows: 6 .omega.seven.columns = f.field_container :description do = f.label :description, t(".description") diff --git a/config/locales/en.yml b/config/locales/en.yml index d3bb4d29601..2f37ca4b5c3 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -4487,6 +4487,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using meta_description: Meta Description meta_keywords: Meta Keywords description: Description + dfc_name: DFC name general_settings: edit: legal_settings: "Legal Settings" diff --git a/db/migrate/20231027041224_add_dfc_name_to_spree_taxons.rb b/db/migrate/20231027041224_add_dfc_name_to_spree_taxons.rb new file mode 100644 index 00000000000..224880511ef --- /dev/null +++ b/db/migrate/20231027041224_add_dfc_name_to_spree_taxons.rb @@ -0,0 +1,5 @@ +class AddDfcNameToSpreeTaxons < ActiveRecord::Migration[7.0] + def change + add_column :spree_taxons, :dfc_name, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 91083ef7e97..e7bb67f638c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -881,6 +881,7 @@ t.string "meta_title", limit: 255 t.string "meta_description", limit: 255 t.string "meta_keywords", limit: 255 + t.string "dfc_name" t.index ["parent_id"], name: "index_taxons_on_parent_id" t.index ["permalink"], name: "index_taxons_on_permalink" t.index ["taxonomy_id"], name: "index_taxons_on_taxonomy_id" From 403386ea094122006e3023fc2bd49e3c01cd5914 Mon Sep 17 00:00:00 2001 From: Gaetan Craig-Riou Date: Tue, 31 Oct 2023 14:35:13 +1100 Subject: [PATCH 03/24] Add basic match on the root level Product Types Currently anything but the leaf level is modelled as a DataFoodConsortium::Connector::SKOSInstance, which isn't supported by the connector as "hasType" for a product. The lead level is modelled by DataFoodConsortium::Connector::SKOSConcept which is supported by connector. On top of is `#narrowers`, `#broaders`and `#prefLabels` aren't set making it very difficult to travers the Product Type tree. --- .../app/services/supplied_product_builder.rb | 22 ++++++++---- .../services/supplied_product_builder_spec.rb | 36 ++++++++++++++++--- 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/engines/dfc_provider/app/services/supplied_product_builder.rb b/engines/dfc_provider/app/services/supplied_product_builder.rb index 69a9871c174..d35ca518ac0 100644 --- a/engines/dfc_provider/app/services/supplied_product_builder.rb +++ b/engines/dfc_provider/app/services/supplied_product_builder.rb @@ -11,19 +11,13 @@ def self.supplied_product(variant) id, name: variant.product_and_full_name, description: variant.description, - productType: product_type, + productType: product_type(variant), quantity: QuantitativeValueBuilder.quantity(variant), spree_product_id: variant.product.id, image_url: variant.product&.image&.url(:product) ) end - # OFN product categories (taxons) are currently not standardised. - # This is just a dummy value for demos. - def self.product_type - DfcLoader.connector.PRODUCT_TYPES.VEGETABLE.NON_LOCAL_VEGETABLE - end - def self.import_variant(supplied_product) product_id = supplied_product.spree_product_id @@ -42,6 +36,7 @@ def self.import_variant(supplied_product) end end + # TODO fix the taxon here def self.import_product(supplied_product) Spree::Product.new( name: supplied_product.name, @@ -62,4 +57,17 @@ def self.apply(supplied_product, variant) QuantitativeValueBuilder.apply(supplied_product.quantity, variant.product) variant.unit_value = variant.product.unit_value end + + def self.product_type(variant) + taxon_name = variant.product.primary_taxon&.dfc_name + + return nil if taxon_name.nil? + + root_product_types = DfcLoader.connector.PRODUCT_TYPES.methods(false).sort + search = root_product_types.index(taxon_name.upcase.to_sym) + + return nil if search.nil? + + DfcLoader.connector.PRODUCT_TYPES.public_send(root_product_types[search]) + end end diff --git a/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb b/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb index 51563b23e77..77fa2ae9e70 100644 --- a/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb +++ b/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb @@ -7,8 +7,12 @@ subject(:builder) { described_class } let(:variant) { - build(:variant, id: 5).tap { |v| v.product.supplier_id = 7 } + build(:variant, id: 5).tap do |v| + v.product.supplier_id = 7 + v.product.primary_taxon = taxon + end } + let(:taxon) { build(:taxon, name: "Drink", dfc_name: "drink") } describe ".supplied_product" do it "assigns a semantic id" do @@ -41,11 +45,33 @@ expect(product.name).to eq "Apple - Granny Smith" end - it "assigns a product type" do - product = builder.supplied_product(variant) - vegetable = DfcLoader.connector.PRODUCT_TYPES.VEGETABLE.NON_LOCAL_VEGETABLE + context "product_type mapping" do + it "assigns a product type" do + product = builder.supplied_product(variant) + drink = DfcLoader.connector.PRODUCT_TYPES.DRINK + + expect(product.productType).to eq drink + end + + context "with non existing product type" do + let(:taxon) { build(:taxon, name: "other", dfc_name: "other") } + + it "returns nil" do + product = builder.supplied_product(variant) + + expect(product.productType).to be_nil + end + end + + context "when no taxon set" do + let(:taxon) { nil } + + it "returns nil" do + product = builder.supplied_product(variant) - expect(product.productType).to eq vegetable + expect(product.productType).to be_nil + end + end end it "assigns an image_url type" do From 8014aa4c2f8b205dd447b8b461d9e306c99e9da7 Mon Sep 17 00:00:00 2001 From: Gaetan Craig-Riou Date: Tue, 31 Oct 2023 14:37:26 +1100 Subject: [PATCH 04/24] Update test to check for Product Type --- engines/dfc_provider/spec/requests/supplied_products_spec.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/engines/dfc_provider/spec/requests/supplied_products_spec.rb b/engines/dfc_provider/spec/requests/supplied_products_spec.rb index 8f39d971b7a..8c8728bd11b 100644 --- a/engines/dfc_provider/spec/requests/supplied_products_spec.rb +++ b/engines/dfc_provider/spec/requests/supplied_products_spec.rb @@ -11,9 +11,11 @@ id: 90_000, supplier: enterprise, name: "Pesto", description: "Basil Pesto", variants: [variant], + primary_taxon: taxon ) } let(:variant) { build(:base_variant, id: 10_001, unit_value: 1) } + let(:taxon) { build(:taxon, name: "Local grocery store", dfc_name: "local_grocery_store") } before { login_as user } @@ -146,6 +148,7 @@ run_test! do expect(response.body).to include variant.name expect(json_response["ofn:spree_product_id"]).to eq 90_000 + expect(json_response["dfc-b:hasType"]).to include("Local grocery store") expect(json_response["ofn:image"]).to include("logo-white.png") # Insert static value to keep documentation deterministic: From 42e2141d050e264c0fbaa94b7f33d8b058fc04e1 Mon Sep 17 00:00:00 2001 From: Gaetan Craig-Riou Date: Fri, 8 Dec 2023 16:23:45 +1100 Subject: [PATCH 05/24] Match product taxon with DFC Product type It relies on having dfc_name populated on the given taxon. Matching is as follow: - parse the DFC product types and store in PRODUCT_TYPES if needed - match the dfc_name against PRODUCT_TYPES - call the method returned on the DFC connector --- .../app/services/supplied_product_builder.rb | 47 +++++++++++++++++-- .../services/supplied_product_builder_spec.rb | 29 +++++++++--- 2 files changed, 66 insertions(+), 10 deletions(-) diff --git a/engines/dfc_provider/app/services/supplied_product_builder.rb b/engines/dfc_provider/app/services/supplied_product_builder.rb index d35ca518ac0..b1360b9443a 100644 --- a/engines/dfc_provider/app/services/supplied_product_builder.rb +++ b/engines/dfc_provider/app/services/supplied_product_builder.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class SuppliedProductBuilder < DfcBuilder + PRODUCT_TYPES = {} # rubocop:disable Style/MutableConstant + def self.supplied_product(variant) id = urls.enterprise_supplied_product_url( enterprise_id: variant.product.supplier_id, @@ -63,11 +65,48 @@ def self.product_type(variant) return nil if taxon_name.nil? - root_product_types = DfcLoader.connector.PRODUCT_TYPES.methods(false).sort - search = root_product_types.index(taxon_name.upcase.to_sym) + populate_product_types if PRODUCT_TYPES.empty? + + return nil if PRODUCT_TYPES[taxon_name.to_sym].nil? + + call_dfc_product_type(PRODUCT_TYPES[taxon_name.to_sym]) + end + + def self.populate_product_types + DfcLoader.connector.PRODUCT_TYPES.topConcepts.each do |product_type| + stack = [] + record_type(stack, product_type.to_s) + end + end + + def self.record_type(stack, product_type) + name = product_type.to_s + current_stack = stack.dup.push(name) + PRODUCT_TYPES[name.downcase.to_sym] = current_stack - return nil if search.nil? + type = call_dfc_product_type(current_stack) - DfcLoader.connector.PRODUCT_TYPES.public_send(root_product_types[search]) + # Narrower product types are defined as class method on the current product type object + narrowers = type.methods(false).sort + + # Leaf node + return if narrowers.empty? + + narrowers.each do |narrower| + # recursive call + record_type(current_stack, narrower) + end + end + + # Callproduct type method ie: DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK + def self.call_dfc_product_type(product_type_path) + type = DfcLoader.connector.PRODUCT_TYPES + product_type_path.each do |pt| + type = type.public_send(pt) + end + + type end + + private_class_method :product_type, :populate_product_types, :record_type end diff --git a/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb b/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb index 77fa2ae9e70..40ba5cc3e29 100644 --- a/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb +++ b/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb @@ -46,19 +46,38 @@ end context "product_type mapping" do - it "assigns a product type" do - product = builder.supplied_product(variant) + subject(:product) { builder.supplied_product(variant) } + + it "assigns a top level product type" do drink = DfcLoader.connector.PRODUCT_TYPES.DRINK expect(product.productType).to eq drink end + context "with second level product type" do + let(:taxon) { build(:taxon, name: "Soft Drink", dfc_name: "soft_drink") } + + it "assigns a second level product type" do + soft_drink = DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK + + expect(product.productType).to eq soft_drink + end + end + + context "with leaf level product type" do + let(:taxon) { build(:taxon, name: "Lemonade", dfc_name: "lemonade") } + + it "assigns a leaf level product type" do + lemonade = DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK.LEMONADE + + expect(product.productType).to eq lemonade + end + end + context "with non existing product type" do let(:taxon) { build(:taxon, name: "other", dfc_name: "other") } it "returns nil" do - product = builder.supplied_product(variant) - expect(product.productType).to be_nil end end @@ -67,8 +86,6 @@ let(:taxon) { nil } it "returns nil" do - product = builder.supplied_product(variant) - expect(product.productType).to be_nil end end From 4d52e169e2768a0d3d0fa88045a513c69c76da3d Mon Sep 17 00:00:00 2001 From: Gaetan Craig-Riou Date: Mon, 11 Dec 2023 11:30:59 +1100 Subject: [PATCH 06/24] Update taxon to not be a top concept product type This is to test the product type matching is working properly --- engines/dfc_provider/spec/requests/supplied_products_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engines/dfc_provider/spec/requests/supplied_products_spec.rb b/engines/dfc_provider/spec/requests/supplied_products_spec.rb index 8c8728bd11b..4211fb0552a 100644 --- a/engines/dfc_provider/spec/requests/supplied_products_spec.rb +++ b/engines/dfc_provider/spec/requests/supplied_products_spec.rb @@ -15,7 +15,7 @@ ) } let(:variant) { build(:base_variant, id: 10_001, unit_value: 1) } - let(:taxon) { build(:taxon, name: "Local grocery store", dfc_name: "local_grocery_store") } + let(:taxon) { build(:taxon, name: "Processed Vegetable", dfc_name: "processed_vegetable") } before { login_as user } @@ -148,7 +148,7 @@ run_test! do expect(response.body).to include variant.name expect(json_response["ofn:spree_product_id"]).to eq 90_000 - expect(json_response["dfc-b:hasType"]).to include("Local grocery store") + expect(json_response["dfc-b:hasType"]).to eq("dfc-pt:processed-vegetable") expect(json_response["ofn:image"]).to include("logo-white.png") # Insert static value to keep documentation deterministic: From a2cb1f4c37a0d62995ac2527e73149e1c404ef02 Mon Sep 17 00:00:00 2001 From: Gaetan Craig-Riou Date: Thu, 14 Dec 2023 15:29:12 +1100 Subject: [PATCH 07/24] Update swagger documentation --- engines/dfc_provider/spec/requests/catalog_items_spec.rb | 1 + engines/dfc_provider/spec/requests/enterprises_spec.rb | 1 + swagger/dfc.yaml | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/engines/dfc_provider/spec/requests/catalog_items_spec.rb b/engines/dfc_provider/spec/requests/catalog_items_spec.rb index c082630044b..367e92d0b41 100644 --- a/engines/dfc_provider/spec/requests/catalog_items_spec.rb +++ b/engines/dfc_provider/spec/requests/catalog_items_spec.rb @@ -17,6 +17,7 @@ :base_product, id: 90_000, supplier: enterprise, name: "Apple", description: "Red", variants: [variant], + primary_taxon: build(:taxon, name: "Non local vegetable", dfc_name: "non_local_vegetable"), ) } let(:variant) { build(:base_variant, id: 10_001, unit_value: 1, sku: "AR") } diff --git a/engines/dfc_provider/spec/requests/enterprises_spec.rb b/engines/dfc_provider/spec/requests/enterprises_spec.rb index e043610dd8c..1b7e7da393a 100644 --- a/engines/dfc_provider/spec/requests/enterprises_spec.rb +++ b/engines/dfc_provider/spec/requests/enterprises_spec.rb @@ -29,6 +29,7 @@ :product_with_image, id: 90_000, supplier: enterprise, name: "Apple", description: "Round", variants: [variant], + primary_taxon: build(:taxon, name: "Non local vegetable", dfc_name: "non_local_vegetable"), ) } let(:variant) { build(:base_variant, id: 10_001, unit_value: 1, sku: "APP") } diff --git a/swagger/dfc.yaml b/swagger/dfc.yaml index 6751e2ec05e..9db45ed5754 100644 --- a/swagger/dfc.yaml +++ b/swagger/dfc.yaml @@ -602,7 +602,7 @@ paths: "@type": dfc-b:SuppliedProduct dfc-b:name: Pesto - 1g dfc-b:description: Basil Pesto - dfc-b:hasType: dfc-pt:non-local-vegetable + dfc-b:hasType: dfc-pt:processed-vegetable dfc-b:hasQuantity: "@type": dfc-b:QuantitativeValue dfc-b:hasUnit: dfc-m:Gram From 2be1aea64cb25017d9dbaab11fed5024b44ba83b Mon Sep 17 00:00:00 2001 From: Gaetan Craig-Riou Date: Thu, 14 Dec 2023 15:43:15 +1100 Subject: [PATCH 08/24] SKOS parser, user shorten version for product types URI --- .../lib/data_food_consortium/connector/skos_parser.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/engines/dfc_provider/lib/data_food_consortium/connector/skos_parser.rb b/engines/dfc_provider/lib/data_food_consortium/connector/skos_parser.rb index dda1422ac2f..3dc4675e35b 100644 --- a/engines/dfc_provider/lib/data_food_consortium/connector/skos_parser.rb +++ b/engines/dfc_provider/lib/data_food_consortium/connector/skos_parser.rb @@ -90,8 +90,13 @@ def createSKOSConcept(element) # rubocop:disable Naming/MethodName prefLabels: element.label ) skosConcept.semanticType = element.type + # Gaetan's fix for productTypes + id = element.id.sub( + "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#", + "dfc-pt:" + ) # Maikel's patch - self.class.concepts[element.id] = skosConcept + self.class.concepts[id] = skosConcept skosConcept end From 476f3b8a281c75a09dbb8fa0954b696ca82610c7 Mon Sep 17 00:00:00 2001 From: Gaetan Craig-Riou Date: Fri, 15 Dec 2023 11:23:39 +1100 Subject: [PATCH 09/24] Fix DFC importer to support Product types --- .../connector/importer.rb | 26 +++++++++++++++---- .../connector/importer_spec.rb | 21 ++++++++++++--- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/engines/dfc_provider/lib/data_food_consortium/connector/importer.rb b/engines/dfc_provider/lib/data_food_consortium/connector/importer.rb index 4e7d2b289ab..354fe7bf2a8 100644 --- a/engines/dfc_provider/lib/data_food_consortium/connector/importer.rb +++ b/engines/dfc_provider/lib/data_food_consortium/connector/importer.rb @@ -4,7 +4,7 @@ module DataFoodConsortium module Connector - class Importer + class Importer # rubocop:disable Metrics/ClassLength TYPES = [ DataFoodConsortium::Connector::CatalogItem, DataFoodConsortium::Connector::Enterprise, @@ -106,7 +106,7 @@ def apply_statement(statement) if property.value.is_a?(Enumerable) property.value << value else - setter = guess_setter_name(statement.predicate) + setter = guess_setter_name(statement) subject.try(setter, value) if setter end end @@ -120,18 +120,26 @@ def resolve_object(object) end def skos_concept(object) - return unless object.uri? - id = object.value.sub( "http://static.datafoodconsortium.org/data/measures.rdf#", "dfc-m:" ).sub( "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/measures.rdf#", "dfc-m:" + ).sub( + "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#", + "dfc-pt:" ) + SKOSParser.concepts[id] end - def guess_setter_name(predicate) + def guess_setter_name(statement) + predicate = statement.predicate + + # Ideally the product models would be consitent with the rule below and use "type" + # instead of "productType" but alast they are not so we need this exception + return "productType=" if predicate.fragment == "hasType" && product_type?(statement) + name = # Some predicates are named like `hasQuantity` # but the attribute name would be `quantity`. @@ -141,6 +149,14 @@ def guess_setter_name(predicate) "#{name}=" end + + def product_type?(statement) + return true if statement.object.literal? && statement.object.value.match("dfc-pt") + + return true if statement.object.path.match("productTypes") + + false + end end end end diff --git a/engines/dfc_provider/spec/lib/data_food_consortium/connector/importer_spec.rb b/engines/dfc_provider/spec/lib/data_food_consortium/connector/importer_spec.rb index 7da3fdd88bb..e2924688186 100644 --- a/engines/dfc_provider/spec/lib/data_food_consortium/connector/importer_spec.rb +++ b/engines/dfc_provider/spec/lib/data_food_consortium/connector/importer_spec.rb @@ -23,6 +23,7 @@ name: "Tomato", description: "Awesome tomato", totalTheoreticalStock: 3, + productType: non_local_vegetable, ) end let(:product_data) do @@ -36,7 +37,8 @@ "dfc-b:alcoholPercentage":0.0, "dfc-b:lifetime":"", "dfc-b:usageOrStorageCondition":"", - "dfc-b:totalTheoreticalStock":3 + "dfc-b:totalTheoreticalStock":3, + "dfc-b:hasType": "dfc-pt:non-local-vegetable" } JSON end @@ -55,7 +57,8 @@ "dfc-b:alcoholPercentage":0.0, "dfc-b:lifetime":"", "dfc-b:usageOrStorageCondition":"", - "dfc-b:totalTheoreticalStock":3 + "dfc-b:totalTheoreticalStock":3, + "dfc-b:hasType": "dfc-pt:non-local-vegetable" } JSON end @@ -74,7 +77,8 @@ "dfc-b:alcoholPercentage":0.0, "dfc-b:lifetime":"", "dfc-b:usageOrStorageCondition":"", - "dfc-b:totalTheoreticalStock":3 + "dfc-b:totalTheoreticalStock":3, + "dfc-b:hasType": "dfc-pt:non-local-vegetable" } JSON end @@ -96,6 +100,11 @@ end connector.MEASURES.PIECE end + let(:non_local_vegetable) do + connector.PRODUCT_TYPES.VEGETABLE.NON_LOCAL_VEGETABLE + end + + before { connector.loadProductTypes(read_file("productTypes")) } it "imports a single object with simple properties" do result = import(product) @@ -105,6 +114,7 @@ expect(result.semanticId).to eq "https://example.net/tomato" expect(result.name).to eq "Tomato" expect(result.description).to eq "Awesome tomato" + expect(result.productType).to eq non_local_vegetable expect(result.totalTheoreticalStock).to eq 3 end @@ -116,6 +126,7 @@ expect(result.semanticId).to eq "https://example.net/tomato" expect(result.name).to eq "Tomato" expect(result.description).to eq "Awesome tomato" + expect(result.productType).to eq non_local_vegetable expect(result.totalTheoreticalStock).to eq 3 end @@ -127,6 +138,7 @@ expect(result.semanticId).to eq "https://example.net/tomato" expect(result.name).to eq "Tomato" expect(result.description).to eq "Awesome tomato" + expect(result.productType).to eq non_local_vegetable expect(result.totalTheoreticalStock).to eq 3 end @@ -138,6 +150,7 @@ expect(result.semanticId).to eq "https://example.net/tomato" expect(result.name).to eq "Tomato" expect(result.description).to eq "Awesome tomato" + expect(result.productType).to eq non_local_vegetable expect(result.totalTheoreticalStock).to eq 3 end @@ -154,6 +167,7 @@ expect(item.semanticId).to eq "https://example.net/tomatoItem" expect(tomato.name).to eq "Tomato" expect(tomato.description).to eq "Awesome tomato" + expect(tomato.productType).to eq non_local_vegetable expect(tomato.totalTheoreticalStock).to eq 3 end @@ -164,6 +178,7 @@ expect(tomato.name).to eq "Tomato" expect(tomato.quantity).to eq items + expect(tomato.productType).to eq non_local_vegetable expect(items.value).to eq 5 expect(items.unit).to eq piece end From e917b26e9115c57c80f73e3de5df29468eb5a8d4 Mon Sep 17 00:00:00 2001 From: Gaetan Craig-Riou Date: Fri, 15 Dec 2023 13:12:36 +1100 Subject: [PATCH 10/24] Support non "underscore" name for dfc_name Ie: you can now use "Soft drink" instead of "soft_drink", case is also ignored --- .../dfc_provider/app/services/supplied_product_builder.rb | 5 +++-- engines/dfc_provider/spec/requests/catalog_items_spec.rb | 2 +- engines/dfc_provider/spec/requests/enterprises_spec.rb | 2 +- engines/dfc_provider/spec/requests/supplied_products_spec.rb | 2 +- .../spec/services/supplied_product_builder_spec.rb | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/engines/dfc_provider/app/services/supplied_product_builder.rb b/engines/dfc_provider/app/services/supplied_product_builder.rb index b1360b9443a..3814e982128 100644 --- a/engines/dfc_provider/app/services/supplied_product_builder.rb +++ b/engines/dfc_provider/app/services/supplied_product_builder.rb @@ -67,9 +67,10 @@ def self.product_type(variant) populate_product_types if PRODUCT_TYPES.empty? - return nil if PRODUCT_TYPES[taxon_name.to_sym].nil? + name = taxon_name.downcase.gsub(" ", "_").to_sym + return nil if PRODUCT_TYPES[name].nil? - call_dfc_product_type(PRODUCT_TYPES[taxon_name.to_sym]) + call_dfc_product_type(PRODUCT_TYPES[name]) end def self.populate_product_types diff --git a/engines/dfc_provider/spec/requests/catalog_items_spec.rb b/engines/dfc_provider/spec/requests/catalog_items_spec.rb index 367e92d0b41..27249778de5 100644 --- a/engines/dfc_provider/spec/requests/catalog_items_spec.rb +++ b/engines/dfc_provider/spec/requests/catalog_items_spec.rb @@ -17,7 +17,7 @@ :base_product, id: 90_000, supplier: enterprise, name: "Apple", description: "Red", variants: [variant], - primary_taxon: build(:taxon, name: "Non local vegetable", dfc_name: "non_local_vegetable"), + primary_taxon: build(:taxon, name: "Non local vegetable", dfc_name: "non local vegetable"), ) } let(:variant) { build(:base_variant, id: 10_001, unit_value: 1, sku: "AR") } diff --git a/engines/dfc_provider/spec/requests/enterprises_spec.rb b/engines/dfc_provider/spec/requests/enterprises_spec.rb index 1b7e7da393a..5a604ff463a 100644 --- a/engines/dfc_provider/spec/requests/enterprises_spec.rb +++ b/engines/dfc_provider/spec/requests/enterprises_spec.rb @@ -29,7 +29,7 @@ :product_with_image, id: 90_000, supplier: enterprise, name: "Apple", description: "Round", variants: [variant], - primary_taxon: build(:taxon, name: "Non local vegetable", dfc_name: "non_local_vegetable"), + primary_taxon: build(:taxon, name: "Non local vegetable", dfc_name: "non local vegetable"), ) } let(:variant) { build(:base_variant, id: 10_001, unit_value: 1, sku: "APP") } diff --git a/engines/dfc_provider/spec/requests/supplied_products_spec.rb b/engines/dfc_provider/spec/requests/supplied_products_spec.rb index 4211fb0552a..219a0e46923 100644 --- a/engines/dfc_provider/spec/requests/supplied_products_spec.rb +++ b/engines/dfc_provider/spec/requests/supplied_products_spec.rb @@ -15,7 +15,7 @@ ) } let(:variant) { build(:base_variant, id: 10_001, unit_value: 1) } - let(:taxon) { build(:taxon, name: "Processed Vegetable", dfc_name: "processed_vegetable") } + let(:taxon) { build(:taxon, name: "Processed Vegetable", dfc_name: "processed vegetable") } before { login_as user } diff --git a/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb b/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb index 40ba5cc3e29..2e213a0525c 100644 --- a/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb +++ b/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb @@ -55,7 +55,7 @@ end context "with second level product type" do - let(:taxon) { build(:taxon, name: "Soft Drink", dfc_name: "soft_drink") } + let(:taxon) { build(:taxon, name: "Soft Drink", dfc_name: "Soft drink") } it "assigns a second level product type" do soft_drink = DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK From 47cea0af6bdd4686a33e49c33811e59ac14e0c2d Mon Sep 17 00:00:00 2001 From: Gaetan Craig-Riou Date: Fri, 15 Dec 2023 13:29:18 +1100 Subject: [PATCH 11/24] Add matching DFC product type to taxon when importing product --- .../app/services/supplied_product_builder.rb | 16 +++++-- .../services/supplied_product_builder_spec.rb | 44 +++++++++++++++++++ 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/engines/dfc_provider/app/services/supplied_product_builder.rb b/engines/dfc_provider/app/services/supplied_product_builder.rb index 3814e982128..970bef97b7c 100644 --- a/engines/dfc_provider/app/services/supplied_product_builder.rb +++ b/engines/dfc_provider/app/services/supplied_product_builder.rb @@ -38,13 +38,12 @@ def self.import_variant(supplied_product) end end - # TODO fix the taxon here def self.import_product(supplied_product) Spree::Product.new( name: supplied_product.name, description: supplied_product.description, price: 0, # will be in DFC Offer - primary_taxon: Spree::Taxon.first, # dummy value until we have a mapping + primary_taxon: taxon(supplied_product) ).tap do |product| QuantitativeValueBuilder.apply(supplied_product.quantity, product) end @@ -109,5 +108,16 @@ def self.call_dfc_product_type(product_type_path) type end - private_class_method :product_type, :populate_product_types, :record_type + def self.taxon(supplied_product) + # We use english locale, might need to make this configurable + dfc_name = supplied_product.productType.prefLabels[:en].downcase + taxon = Spree::Taxon.find_by(dfc_name: ) + + return taxon if taxon.present? + + nil + end + + private_class_method :product_type, :populate_product_types, :record_type, :call_dfc_product_type, + :taxon end diff --git a/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb b/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb index 2e213a0525c..1c46bc7f13a 100644 --- a/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb +++ b/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb @@ -102,4 +102,48 @@ expect(product.image).to eq variant.product.image.url(:product) end end + + describe ".import_product" do + let(:supplied_product) do + DataFoodConsortium::Connector::SuppliedProduct.new( + "https://example.net/tomato", + name: "Tomato", + description: "Awesome tomato", + quantity: DataFoodConsortium::Connector::QuantitativeValue.new( + unit: DfcLoader.connector.MEASURES.KILOGRAM, + value: 2, + ), + productType: product_type, + ) + end + let(:product_type) { DfcLoader.connector.PRODUCT_TYPES.VEGETABLE.NON_LOCAL_VEGETABLE } + let!(:taxon) { create(:taxon, name: "Non local vegetable", dfc_name: "non local vegetable") } + + it "creates a new Spree::Product" do + product = builder.import_product(supplied_product) + + expect(product).to be_a(Spree::Product) + expect(product.name).to eq("Tomato") + expect(product.description).to eq("Awesome tomato") + expect(product.variant_unit).to eq("weight") + end + + describe "taxon" do + it "assigns the taxon matching the DFC product type" do + product = builder.import_product(supplied_product) + + expect(product.primary_taxon).to eq(taxon) + end + + describe "when no matching taxon" do + let(:product_type) { DfcLoader.connector.PRODUCT_TYPES.DRINK } + + it "set the taxon to nil" do + product = builder.import_product(supplied_product) + + expect(product.primary_taxon).to be_nil + end + end + end + end end From a35e896a988465e16b4b6a7b4b34b3a9e37d1e03 Mon Sep 17 00:00:00 2001 From: Gaetan Craig-Riou Date: Fri, 15 Dec 2023 13:44:27 +1100 Subject: [PATCH 12/24] Add taxon to request spec --- engines/dfc_provider/spec/requests/supplied_products_spec.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/engines/dfc_provider/spec/requests/supplied_products_spec.rb b/engines/dfc_provider/spec/requests/supplied_products_spec.rb index 219a0e46923..745d0de9ea0 100644 --- a/engines/dfc_provider/spec/requests/supplied_products_spec.rb +++ b/engines/dfc_provider/spec/requests/supplied_products_spec.rb @@ -16,6 +16,9 @@ } let(:variant) { build(:base_variant, id: 10_001, unit_value: 1) } let(:taxon) { build(:taxon, name: "Processed Vegetable", dfc_name: "processed vegetable") } + let!(:non_local_vegetable) { + create(:taxon, name: "Non Local Vegetable", dfc_name: "non local vegetable") + } before { login_as user } @@ -102,6 +105,7 @@ product = Spree::Product.find(product_id) expect(product.name).to eq "Apple" expect(product.variants).to eq [variant] + expect(product.primary_taxon).to eq(non_local_vegetable) # Creates a variant for existing product supplied_product[:'ofn:spree_product_id'] = product_id From 25a820fe122f81cb8f158a1f5dc22d14d4e2cc14 Mon Sep 17 00:00:00 2001 From: Gaetan Craig-Riou Date: Fri, 15 Dec 2023 14:04:03 +1100 Subject: [PATCH 13/24] Allow updating of product type --- .../dfc_provider/app/services/supplied_product_builder.rb | 1 + .../spec/fixtures/files/put_supplied_product.json | 2 +- engines/dfc_provider/spec/requests/supplied_products_spec.rb | 5 +++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/engines/dfc_provider/app/services/supplied_product_builder.rb b/engines/dfc_provider/app/services/supplied_product_builder.rb index 970bef97b7c..5596cb9593d 100644 --- a/engines/dfc_provider/app/services/supplied_product_builder.rb +++ b/engines/dfc_provider/app/services/supplied_product_builder.rb @@ -52,6 +52,7 @@ def self.import_product(supplied_product) def self.apply(supplied_product, variant) variant.product.assign_attributes( description: supplied_product.description, + primary_taxon: taxon(supplied_product) ) variant.display_name = supplied_product.name diff --git a/engines/dfc_provider/spec/fixtures/files/put_supplied_product.json b/engines/dfc_provider/spec/fixtures/files/put_supplied_product.json index cc081912bbe..d3e69dbb67a 100644 --- a/engines/dfc_provider/spec/fixtures/files/put_supplied_product.json +++ b/engines/dfc_provider/spec/fixtures/files/put_supplied_product.json @@ -93,7 +93,7 @@ "dfc-b:hasUnit": "dfc-m:Piece", "dfc-b:value": 17 }, - "dfc-b:hasType": "dfc-pt:non-local-vegetable", + "dfc-b:hasType": "dfc-pt:drink", "dfc-b:lifetime": "", "dfc-b:name": "Pesto novo", "dfc-b:totalTheoreticalStock": 0, diff --git a/engines/dfc_provider/spec/requests/supplied_products_spec.rb b/engines/dfc_provider/spec/requests/supplied_products_spec.rb index 745d0de9ea0..d3a63e40ee5 100644 --- a/engines/dfc_provider/spec/requests/supplied_products_spec.rb +++ b/engines/dfc_provider/spec/requests/supplied_products_spec.rb @@ -172,6 +172,10 @@ end put "Update SuppliedProduct" do + let!(:drink_taxon) { + create(:taxon, name: "Drink", dfc_name: "drink") + } + consumes "application/json" parameter name: :supplied_product, in: :body, schema: { @@ -197,6 +201,7 @@ }.to change { variant.description }.to("DFC-Pesto updated") .and change { variant.display_name }.to("Pesto novo") .and change { variant.unit_value }.to(17) + .and change { variant.product.primary_taxon }.to(drink_taxon) end end end From 162fd4bef5650c9d8ccb6f8d2a721a94fe550f7e Mon Sep 17 00:00:00 2001 From: Gaetan Craig-Riou Date: Fri, 15 Dec 2023 14:07:04 +1100 Subject: [PATCH 14/24] update documentation --- swagger/dfc.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swagger/dfc.yaml b/swagger/dfc.yaml index 9db45ed5754..fc70f05a7a4 100644 --- a/swagger/dfc.yaml +++ b/swagger/dfc.yaml @@ -698,7 +698,7 @@ paths: "@type": dfc-b:QuantitativeValue dfc-b:hasUnit: dfc-m:Piece dfc-b:value: 17 - dfc-b:hasType: dfc-pt:non-local-vegetable + dfc-b:hasType: dfc-pt:drink dfc-b:lifetime: '' dfc-b:name: Pesto novo dfc-b:totalTheoreticalStock: 0 From 8013fac5b860c80f69eb2345a5f698b91c3fb75e Mon Sep 17 00:00:00 2001 From: Gaetan Craig-Riou Date: Mon, 18 Dec 2023 14:09:53 +1100 Subject: [PATCH 15/24] Per review, match on the product type URI instead of name Fox export only --- ...1027041224_add_dfc_name_to_spree_taxons.rb | 2 +- db/schema.rb | 2 +- .../app/services/supplied_product_builder.rb | 17 +++++------ .../spec/requests/catalog_items_spec.rb | 9 +++++- .../spec/requests/enterprises_spec.rb | 9 +++++- .../spec/requests/supplied_products_spec.rb | 17 +++++++++-- .../services/supplied_product_builder_spec.rb | 28 +++++++++++++++---- 7 files changed, 64 insertions(+), 20 deletions(-) diff --git a/db/migrate/20231027041224_add_dfc_name_to_spree_taxons.rb b/db/migrate/20231027041224_add_dfc_name_to_spree_taxons.rb index 224880511ef..22ae971b702 100644 --- a/db/migrate/20231027041224_add_dfc_name_to_spree_taxons.rb +++ b/db/migrate/20231027041224_add_dfc_name_to_spree_taxons.rb @@ -1,5 +1,5 @@ class AddDfcNameToSpreeTaxons < ActiveRecord::Migration[7.0] def change - add_column :spree_taxons, :dfc_name, :string + add_column :spree_taxons, :dfc_id, :string end end diff --git a/db/schema.rb b/db/schema.rb index e7bb67f638c..5a780963365 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -881,7 +881,7 @@ t.string "meta_title", limit: 255 t.string "meta_description", limit: 255 t.string "meta_keywords", limit: 255 - t.string "dfc_name" + t.string "dfc_id" t.index ["parent_id"], name: "index_taxons_on_parent_id" t.index ["permalink"], name: "index_taxons_on_permalink" t.index ["taxonomy_id"], name: "index_taxons_on_taxonomy_id" diff --git a/engines/dfc_provider/app/services/supplied_product_builder.rb b/engines/dfc_provider/app/services/supplied_product_builder.rb index 5596cb9593d..5bfa74aad5c 100644 --- a/engines/dfc_provider/app/services/supplied_product_builder.rb +++ b/engines/dfc_provider/app/services/supplied_product_builder.rb @@ -61,16 +61,15 @@ def self.apply(supplied_product, variant) end def self.product_type(variant) - taxon_name = variant.product.primary_taxon&.dfc_name + taxon_dfc_id = variant.product.primary_taxon&.dfc_id - return nil if taxon_name.nil? + return nil if taxon_dfc_id.nil? populate_product_types if PRODUCT_TYPES.empty? - name = taxon_name.downcase.gsub(" ", "_").to_sym - return nil if PRODUCT_TYPES[name].nil? + return nil if PRODUCT_TYPES[taxon_dfc_id].nil? - call_dfc_product_type(PRODUCT_TYPES[name]) + call_dfc_product_type(PRODUCT_TYPES[taxon_dfc_id]) end def self.populate_product_types @@ -83,10 +82,12 @@ def self.populate_product_types def self.record_type(stack, product_type) name = product_type.to_s current_stack = stack.dup.push(name) - PRODUCT_TYPES[name.downcase.to_sym] = current_stack type = call_dfc_product_type(current_stack) + id = type.semanticId + PRODUCT_TYPES[id] = current_stack + # Narrower product types are defined as class method on the current product type object narrowers = type.methods(false).sort @@ -111,8 +112,8 @@ def self.call_dfc_product_type(product_type_path) def self.taxon(supplied_product) # We use english locale, might need to make this configurable - dfc_name = supplied_product.productType.prefLabels[:en].downcase - taxon = Spree::Taxon.find_by(dfc_name: ) + dfc_id = supplied_product.productType.prefLabels[:en].downcase + taxon = Spree::Taxon.find_by(dfc_id: ) return taxon if taxon.present? diff --git a/engines/dfc_provider/spec/requests/catalog_items_spec.rb b/engines/dfc_provider/spec/requests/catalog_items_spec.rb index 27249778de5..f047812e005 100644 --- a/engines/dfc_provider/spec/requests/catalog_items_spec.rb +++ b/engines/dfc_provider/spec/requests/catalog_items_spec.rb @@ -17,7 +17,14 @@ :base_product, id: 90_000, supplier: enterprise, name: "Apple", description: "Red", variants: [variant], - primary_taxon: build(:taxon, name: "Non local vegetable", dfc_name: "non local vegetable"), + primary_taxon: non_local_vegetable + ) + } + let(:non_local_vegetable) { + build( + :taxon, + name: "Non Local Vegetable", + dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#non-local-vegetable" ) } let(:variant) { build(:base_variant, id: 10_001, unit_value: 1, sku: "AR") } diff --git a/engines/dfc_provider/spec/requests/enterprises_spec.rb b/engines/dfc_provider/spec/requests/enterprises_spec.rb index 5a604ff463a..ca875360aed 100644 --- a/engines/dfc_provider/spec/requests/enterprises_spec.rb +++ b/engines/dfc_provider/spec/requests/enterprises_spec.rb @@ -29,7 +29,14 @@ :product_with_image, id: 90_000, supplier: enterprise, name: "Apple", description: "Round", variants: [variant], - primary_taxon: build(:taxon, name: "Non local vegetable", dfc_name: "non local vegetable"), + primary_taxon: non_local_vegetable + ) + } + let(:non_local_vegetable) { + build( + :taxon, + name: "Non Local Vegetable", + dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#non-local-vegetable" ) } let(:variant) { build(:base_variant, id: 10_001, unit_value: 1, sku: "APP") } diff --git a/engines/dfc_provider/spec/requests/supplied_products_spec.rb b/engines/dfc_provider/spec/requests/supplied_products_spec.rb index d3a63e40ee5..f0e37925527 100644 --- a/engines/dfc_provider/spec/requests/supplied_products_spec.rb +++ b/engines/dfc_provider/spec/requests/supplied_products_spec.rb @@ -15,9 +15,20 @@ ) } let(:variant) { build(:base_variant, id: 10_001, unit_value: 1) } - let(:taxon) { build(:taxon, name: "Processed Vegetable", dfc_name: "processed vegetable") } + let(:taxon) { + build( + :taxon, + name: "Processed Vegetable", + dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#processed-vegetable" + ) + } + let!(:non_local_vegetable) { - create(:taxon, name: "Non Local Vegetable", dfc_name: "non local vegetable") + create( + :taxon, + name: "Non Local Vegetable", + dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#non-local-vegetable" + ) } before { login_as user } @@ -173,7 +184,7 @@ put "Update SuppliedProduct" do let!(:drink_taxon) { - create(:taxon, name: "Drink", dfc_name: "drink") + create(:taxon, name: "Drink", dfc_id: "drink") } consumes "application/json" diff --git a/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb b/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb index 1c46bc7f13a..977c64d256a 100644 --- a/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb +++ b/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb @@ -12,7 +12,13 @@ v.product.primary_taxon = taxon end } - let(:taxon) { build(:taxon, name: "Drink", dfc_name: "drink") } + let(:taxon) { + build( + :taxon, + name: "Drink", + dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#drink" + ) + } describe ".supplied_product" do it "assigns a semantic id" do @@ -55,7 +61,13 @@ end context "with second level product type" do - let(:taxon) { build(:taxon, name: "Soft Drink", dfc_name: "Soft drink") } + let(:taxon) { + build( + :taxon, + name: "Soft Drink", + dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#soft-drink" + ) + } it "assigns a second level product type" do soft_drink = DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK @@ -65,7 +77,13 @@ end context "with leaf level product type" do - let(:taxon) { build(:taxon, name: "Lemonade", dfc_name: "lemonade") } + let(:taxon) { + build( + :taxon, + name: "Lemonade", + dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#lemonade" + ) + } it "assigns a leaf level product type" do lemonade = DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK.LEMONADE @@ -75,7 +93,7 @@ end context "with non existing product type" do - let(:taxon) { build(:taxon, name: "other", dfc_name: "other") } + let(:taxon) { build(:taxon, name: "other", dfc_id: "other") } it "returns nil" do expect(product.productType).to be_nil @@ -117,7 +135,7 @@ ) end let(:product_type) { DfcLoader.connector.PRODUCT_TYPES.VEGETABLE.NON_LOCAL_VEGETABLE } - let!(:taxon) { create(:taxon, name: "Non local vegetable", dfc_name: "non local vegetable") } + let!(:taxon) { create(:taxon, name: "Non local vegetable", dfc_id: "non local vegetable") } it "creates a new Spree::Product" do product = builder.import_product(supplied_product) From d8b56d5c16154617c9e1368b937c981d9763fd81 Mon Sep 17 00:00:00 2001 From: Gaetan Craig-Riou Date: Mon, 18 Dec 2023 14:20:31 +1100 Subject: [PATCH 16/24] Per review, match on the product type URI instead of name part 2 For import this time --- .../app/services/supplied_product_builder.rb | 9 ++------- .../dfc_provider/spec/requests/supplied_products_spec.rb | 6 +++++- .../spec/services/supplied_product_builder_spec.rb | 8 +++++++- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/engines/dfc_provider/app/services/supplied_product_builder.rb b/engines/dfc_provider/app/services/supplied_product_builder.rb index 5bfa74aad5c..7d04a8413fd 100644 --- a/engines/dfc_provider/app/services/supplied_product_builder.rb +++ b/engines/dfc_provider/app/services/supplied_product_builder.rb @@ -111,13 +111,8 @@ def self.call_dfc_product_type(product_type_path) end def self.taxon(supplied_product) - # We use english locale, might need to make this configurable - dfc_id = supplied_product.productType.prefLabels[:en].downcase - taxon = Spree::Taxon.find_by(dfc_id: ) - - return taxon if taxon.present? - - nil + dfc_id = supplied_product.productType.semanticId + Spree::Taxon.find_by(dfc_id: ) end private_class_method :product_type, :populate_product_types, :record_type, :call_dfc_product_type, diff --git a/engines/dfc_provider/spec/requests/supplied_products_spec.rb b/engines/dfc_provider/spec/requests/supplied_products_spec.rb index f0e37925527..71119396541 100644 --- a/engines/dfc_provider/spec/requests/supplied_products_spec.rb +++ b/engines/dfc_provider/spec/requests/supplied_products_spec.rb @@ -184,7 +184,11 @@ put "Update SuppliedProduct" do let!(:drink_taxon) { - create(:taxon, name: "Drink", dfc_id: "drink") + create( + :taxon, + name: "Drink", + dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#drink" + ) } consumes "application/json" diff --git a/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb b/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb index 977c64d256a..770e72a95fc 100644 --- a/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb +++ b/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb @@ -135,7 +135,13 @@ ) end let(:product_type) { DfcLoader.connector.PRODUCT_TYPES.VEGETABLE.NON_LOCAL_VEGETABLE } - let!(:taxon) { create(:taxon, name: "Non local vegetable", dfc_id: "non local vegetable") } + let!(:taxon) { + create( + :taxon, + name: "Non local vegetable", + dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#non-local-vegetable" + ) + } it "creates a new Spree::Product" do product = builder.import_product(supplied_product) From c40ccb8e86a273fe87fedf013a9911f1adfa9c28 Mon Sep 17 00:00:00 2001 From: Gaetan Craig-Riou Date: Fri, 22 Dec 2023 14:27:27 +1100 Subject: [PATCH 17/24] Refactor, move product type matching to DfcProductTypeFactory It keeps SuppliedProductBuilder and move all the matching logic to its own class --- .../app/services/dfc_product_type_factory.rb | 63 +++++++++++++++++++ .../app/services/supplied_product_builder.rb | 51 +-------------- .../services/dfc_product_type_factory_spec.rb | 49 +++++++++++++++ .../services/supplied_product_builder_spec.rb | 50 ++------------- 4 files changed, 119 insertions(+), 94 deletions(-) create mode 100644 engines/dfc_provider/app/services/dfc_product_type_factory.rb create mode 100644 engines/dfc_provider/spec/services/dfc_product_type_factory_spec.rb diff --git a/engines/dfc_provider/app/services/dfc_product_type_factory.rb b/engines/dfc_provider/app/services/dfc_product_type_factory.rb new file mode 100644 index 00000000000..07c39c06cff --- /dev/null +++ b/engines/dfc_provider/app/services/dfc_product_type_factory.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +require 'singleton' + +class DfcProductTypeFactory + include Singleton + + def self.for(dfc_id) + instance.for(dfc_id) + end + + def initialize + @product_types = {} + + populate_product_types + end + + def for(dfc_id) + return nil if @product_types[dfc_id].nil? + + call_dfc_product_type(@product_types[dfc_id]) + end + + private + + def populate_product_types + DfcLoader.connector.PRODUCT_TYPES.topConcepts.each do |product_type| + stack = [] + record_type(stack, product_type.to_s) + end + end + + def record_type(stack, product_type) + name = product_type.to_s + current_stack = stack.dup.push(name) + + type = call_dfc_product_type(current_stack) + + id = type.semanticId + @product_types[id] = current_stack + + # Narrower product types are defined as class method on the current product type object + narrowers = type.methods(false).sort + + # Leaf node + return if narrowers.empty? + + narrowers.each do |narrower| + # recursive call + record_type(current_stack, narrower) + end + end + + # Callproduct type method ie: DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK + def call_dfc_product_type(product_type_path) + type = DfcLoader.connector.PRODUCT_TYPES + product_type_path.each do |pt| + type = type.public_send(pt) + end + + type + end +end diff --git a/engines/dfc_provider/app/services/supplied_product_builder.rb b/engines/dfc_provider/app/services/supplied_product_builder.rb index 7d04a8413fd..ea475da1eca 100644 --- a/engines/dfc_provider/app/services/supplied_product_builder.rb +++ b/engines/dfc_provider/app/services/supplied_product_builder.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true class SuppliedProductBuilder < DfcBuilder - PRODUCT_TYPES = {} # rubocop:disable Style/MutableConstant - def self.supplied_product(variant) id = urls.enterprise_supplied_product_url( enterprise_id: variant.product.supplier_id, @@ -63,51 +61,7 @@ def self.apply(supplied_product, variant) def self.product_type(variant) taxon_dfc_id = variant.product.primary_taxon&.dfc_id - return nil if taxon_dfc_id.nil? - - populate_product_types if PRODUCT_TYPES.empty? - - return nil if PRODUCT_TYPES[taxon_dfc_id].nil? - - call_dfc_product_type(PRODUCT_TYPES[taxon_dfc_id]) - end - - def self.populate_product_types - DfcLoader.connector.PRODUCT_TYPES.topConcepts.each do |product_type| - stack = [] - record_type(stack, product_type.to_s) - end - end - - def self.record_type(stack, product_type) - name = product_type.to_s - current_stack = stack.dup.push(name) - - type = call_dfc_product_type(current_stack) - - id = type.semanticId - PRODUCT_TYPES[id] = current_stack - - # Narrower product types are defined as class method on the current product type object - narrowers = type.methods(false).sort - - # Leaf node - return if narrowers.empty? - - narrowers.each do |narrower| - # recursive call - record_type(current_stack, narrower) - end - end - - # Callproduct type method ie: DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK - def self.call_dfc_product_type(product_type_path) - type = DfcLoader.connector.PRODUCT_TYPES - product_type_path.each do |pt| - type = type.public_send(pt) - end - - type + DfcProductTypeFactory.for(taxon_dfc_id) end def self.taxon(supplied_product) @@ -115,6 +69,5 @@ def self.taxon(supplied_product) Spree::Taxon.find_by(dfc_id: ) end - private_class_method :product_type, :populate_product_types, :record_type, :call_dfc_product_type, - :taxon + private_class_method :product_type, :taxon end diff --git a/engines/dfc_provider/spec/services/dfc_product_type_factory_spec.rb b/engines/dfc_provider/spec/services/dfc_product_type_factory_spec.rb new file mode 100644 index 00000000000..31096f4f9c1 --- /dev/null +++ b/engines/dfc_provider/spec/services/dfc_product_type_factory_spec.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +require_relative "../spec_helper" + +describe DfcProductTypeFactory do + describe ".for" do + let(:dfc_id) { + "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#drink" + } + + it "assigns a top level product type" do + drink = DfcLoader.connector.PRODUCT_TYPES.DRINK + + expect(described_class.for(dfc_id)).to eq drink + end + + context "with second level product type" do + let(:dfc_id) { + "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#soft-drink" + } + + it "assigns a second level product type" do + soft_drink = DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK + + expect(described_class.for(dfc_id)).to eq soft_drink + end + end + + context "with leaf level product type" do + let(:dfc_id) { + "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#lemonade" + } + + it "assigns a leaf level product type" do + lemonade = DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK.LEMONADE + + expect(described_class.for(dfc_id)).to eq lemonade + end + end + + context "with non existing product type" do + let(:dfc_id) { "other" } + + it "returns nil" do + expect(described_class.for(dfc_id)).to be_nil + end + end + end +end diff --git a/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb b/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb index 770e72a95fc..b8e41df7428 100644 --- a/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb +++ b/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb @@ -15,8 +15,8 @@ let(:taxon) { build( :taxon, - name: "Drink", - dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#drink" + name: "Soft Drink", + dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#soft-drink" ) } @@ -54,50 +54,10 @@ context "product_type mapping" do subject(:product) { builder.supplied_product(variant) } - it "assigns a top level product type" do - drink = DfcLoader.connector.PRODUCT_TYPES.DRINK + it "assigns a product type" do + soft_drink = DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK - expect(product.productType).to eq drink - end - - context "with second level product type" do - let(:taxon) { - build( - :taxon, - name: "Soft Drink", - dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#soft-drink" - ) - } - - it "assigns a second level product type" do - soft_drink = DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK - - expect(product.productType).to eq soft_drink - end - end - - context "with leaf level product type" do - let(:taxon) { - build( - :taxon, - name: "Lemonade", - dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#lemonade" - ) - } - - it "assigns a leaf level product type" do - lemonade = DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK.LEMONADE - - expect(product.productType).to eq lemonade - end - end - - context "with non existing product type" do - let(:taxon) { build(:taxon, name: "other", dfc_id: "other") } - - it "returns nil" do - expect(product.productType).to be_nil - end + expect(product.productType).to eq soft_drink end context "when no taxon set" do From c1e7aa6daa24e4d6f63eae5a5725bf5d607588b2 Mon Sep 17 00:00:00 2001 From: Gaetan Craig-Riou Date: Fri, 22 Dec 2023 15:57:32 +1100 Subject: [PATCH 18/24] Small improvment for Importer Doesn't try to load skos concept if we are not dealing with an object that doesn't refers to a skos concept --- .../lib/data_food_consortium/connector/importer.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/engines/dfc_provider/lib/data_food_consortium/connector/importer.rb b/engines/dfc_provider/lib/data_food_consortium/connector/importer.rb index 354fe7bf2a8..9f13e68c604 100644 --- a/engines/dfc_provider/lib/data_food_consortium/connector/importer.rb +++ b/engines/dfc_provider/lib/data_food_consortium/connector/importer.rb @@ -120,6 +120,10 @@ def resolve_object(object) end def skos_concept(object) + # Sometimes we get given a literal object with a value referring to skos concept + # ie "dfc-pt:drink" + return unless object.value.match(":") || object.uri? + id = object.value.sub( "http://static.datafoodconsortium.org/data/measures.rdf#", "dfc-m:" ).sub( From 9607739e1633de611d85a77cd9f4dfc989b69357 Mon Sep 17 00:00:00 2001 From: Gaetan Craig-Riou Date: Fri, 22 Dec 2023 16:14:39 +1100 Subject: [PATCH 19/24] Per review, store product type instead of path --- .../dfc_provider/app/services/dfc_product_type_factory.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/engines/dfc_provider/app/services/dfc_product_type_factory.rb b/engines/dfc_provider/app/services/dfc_product_type_factory.rb index 07c39c06cff..1ffb67de845 100644 --- a/engines/dfc_provider/app/services/dfc_product_type_factory.rb +++ b/engines/dfc_provider/app/services/dfc_product_type_factory.rb @@ -16,9 +16,7 @@ def initialize end def for(dfc_id) - return nil if @product_types[dfc_id].nil? - - call_dfc_product_type(@product_types[dfc_id]) + @product_types[dfc_id] end private @@ -37,7 +35,7 @@ def record_type(stack, product_type) type = call_dfc_product_type(current_stack) id = type.semanticId - @product_types[id] = current_stack + @product_types[id] = type # Narrower product types are defined as class method on the current product type object narrowers = type.methods(false).sort From 2aa0ab15b9b455ec0dddfcf121999bcbf22fbf8b Mon Sep 17 00:00:00 2001 From: Gaetan Craig-Riou Date: Fri, 22 Dec 2023 17:47:05 +1100 Subject: [PATCH 20/24] Simplify traversing of Product Types --- .../app/services/dfc_product_type_factory.rb | 28 +++++-------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/engines/dfc_provider/app/services/dfc_product_type_factory.rb b/engines/dfc_provider/app/services/dfc_product_type_factory.rb index 1ffb67de845..f41bfe2fe17 100644 --- a/engines/dfc_provider/app/services/dfc_product_type_factory.rb +++ b/engines/dfc_provider/app/services/dfc_product_type_factory.rb @@ -23,39 +23,25 @@ def for(dfc_id) def populate_product_types DfcLoader.connector.PRODUCT_TYPES.topConcepts.each do |product_type| - stack = [] - record_type(stack, product_type.to_s) + record_type(DfcLoader.connector.PRODUCT_TYPES, product_type.to_s) end end - def record_type(stack, product_type) - name = product_type.to_s - current_stack = stack.dup.push(name) + def record_type(product_type_object, product_type) + current_product_type = product_type_object.public_send(product_type.to_s) - type = call_dfc_product_type(current_stack) - - id = type.semanticId - @product_types[id] = type + id = current_product_type.semanticId + @product_types[id] = current_product_type # Narrower product types are defined as class method on the current product type object - narrowers = type.methods(false).sort + narrowers = current_product_type.methods(false).sort # Leaf node return if narrowers.empty? narrowers.each do |narrower| # recursive call - record_type(current_stack, narrower) + record_type(current_product_type, narrower) end end - - # Callproduct type method ie: DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK - def call_dfc_product_type(product_type_path) - type = DfcLoader.connector.PRODUCT_TYPES - product_type_path.each do |pt| - type = type.public_send(pt) - end - - type - end end From 35da321e15b3044220fbafbb479c852b867b29cb Mon Sep 17 00:00:00 2001 From: Gaetan Craig-Riou Date: Thu, 4 Jan 2024 15:47:46 +1100 Subject: [PATCH 21/24] Fix skos parser to not replace Product type URI by dfc-pt The given productType.rdf file doesn't give us any context for `dfc-pt`, so there was no reason to do that. We still need to do some substitution in the importer, as some times we are given `dfc-pt` as input data. --- .../lib/data_food_consortium/connector/importer.rb | 4 ++-- .../lib/data_food_consortium/connector/skos_parser.rb | 7 +------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/engines/dfc_provider/lib/data_food_consortium/connector/importer.rb b/engines/dfc_provider/lib/data_food_consortium/connector/importer.rb index 9f13e68c604..21da91b2a79 100644 --- a/engines/dfc_provider/lib/data_food_consortium/connector/importer.rb +++ b/engines/dfc_provider/lib/data_food_consortium/connector/importer.rb @@ -130,8 +130,8 @@ def skos_concept(object) "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/measures.rdf#", "dfc-m:" ).sub( - "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#", - "dfc-pt:" + "dfc-pt:", + "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#" ) SKOSParser.concepts[id] diff --git a/engines/dfc_provider/lib/data_food_consortium/connector/skos_parser.rb b/engines/dfc_provider/lib/data_food_consortium/connector/skos_parser.rb index 3dc4675e35b..dda1422ac2f 100644 --- a/engines/dfc_provider/lib/data_food_consortium/connector/skos_parser.rb +++ b/engines/dfc_provider/lib/data_food_consortium/connector/skos_parser.rb @@ -90,13 +90,8 @@ def createSKOSConcept(element) # rubocop:disable Naming/MethodName prefLabels: element.label ) skosConcept.semanticType = element.type - # Gaetan's fix for productTypes - id = element.id.sub( - "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#", - "dfc-pt:" - ) # Maikel's patch - self.class.concepts[id] = skosConcept + self.class.concepts[element.id] = skosConcept skosConcept end From 28e17aff68d1a8cedc25698dd83e57e8809728a5 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 5 Jan 2024 13:59:27 +1100 Subject: [PATCH 22/24] Fix DFC context in spec to resolve product types The `dfc-b:hasType` value can only be parsed as object id if the context contains: ``` "dfc-b:hasType":{ "@type":"@id" }, ``` The standard context includes this and it's easier to use. Now that the URIs of product types are correctly resolved, we don't need to substitute the URI manually. Also dropped an old unneeded spec for backwards compatibility. --- .../connector/importer.rb | 7 +--- .../connector/importer_spec.rb | 35 ++----------------- .../spec/requests/supplied_products_spec.rb | 9 +---- swagger/dfc.yaml | 7 +--- 4 files changed, 5 insertions(+), 53 deletions(-) diff --git a/engines/dfc_provider/lib/data_food_consortium/connector/importer.rb b/engines/dfc_provider/lib/data_food_consortium/connector/importer.rb index 21da91b2a79..d668facf3bb 100644 --- a/engines/dfc_provider/lib/data_food_consortium/connector/importer.rb +++ b/engines/dfc_provider/lib/data_food_consortium/connector/importer.rb @@ -120,18 +120,13 @@ def resolve_object(object) end def skos_concept(object) - # Sometimes we get given a literal object with a value referring to skos concept - # ie "dfc-pt:drink" - return unless object.value.match(":") || object.uri? + return unless object.uri? id = object.value.sub( "http://static.datafoodconsortium.org/data/measures.rdf#", "dfc-m:" ).sub( "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/measures.rdf#", "dfc-m:" - ).sub( - "dfc-pt:", - "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#" ) SKOSParser.concepts[id] diff --git a/engines/dfc_provider/spec/lib/data_food_consortium/connector/importer_spec.rb b/engines/dfc_provider/spec/lib/data_food_consortium/connector/importer_spec.rb index e2924688186..465fadac45d 100644 --- a/engines/dfc_provider/spec/lib/data_food_consortium/connector/importer_spec.rb +++ b/engines/dfc_provider/spec/lib/data_food_consortium/connector/importer_spec.rb @@ -42,33 +42,14 @@ } JSON end - let(:product_data_with_context) do - <<~JSON - { - "@context": { - "dfc-b": "http://static.datafoodconsortium.org/ontologies/DFC_BusinessOntology.owl#", - "dfc-m": "http://static.datafoodconsortium.org/data/measures.rdf#", - "dfc-pt": "http://static.datafoodconsortium.org/data/productTypes.rdf#" - }, - "@id":"https://example.net/tomato", - "@type":"dfc-b:SuppliedProduct", - "dfc-b:name":"Tomato", - "dfc-b:description":"Awesome tomato", - "dfc-b:alcoholPercentage":0.0, - "dfc-b:lifetime":"", - "dfc-b:usageOrStorageCondition":"", - "dfc-b:totalTheoreticalStock":3, - "dfc-b:hasType": "dfc-pt:non-local-vegetable" - } - JSON - end let(:product_data_with_context_v1_8) do <<~JSON { "@context": { "dfc-b": "https://github.com/datafoodconsortium/ontology/releases/latest/download/DFC_BusinessOntology.owl#", "dfc-m": "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/measures.rdf#", - "dfc-pt": "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#" + "dfc-pt": "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#", + "dfc-b:hasType":{"@type":"@id"} }, "@id":"https://example.net/tomato", "@type":"dfc-b:SuppliedProduct", @@ -130,18 +111,6 @@ expect(result.totalTheoreticalStock).to eq 3 end - it "imports an object with included context" do - result = connector.import(product_data_with_context) - - expect(result).to be_a DataFoodConsortium::Connector::SuppliedProduct - expect(result.semanticType).to eq "dfc-b:SuppliedProduct" - expect(result.semanticId).to eq "https://example.net/tomato" - expect(result.name).to eq "Tomato" - expect(result.description).to eq "Awesome tomato" - expect(result.productType).to eq non_local_vegetable - expect(result.totalTheoreticalStock).to eq 3 - end - it "imports an object with DFC v1.8 context" do result = connector.import(product_data_with_context_v1_8) diff --git a/engines/dfc_provider/spec/requests/supplied_products_spec.rb b/engines/dfc_provider/spec/requests/supplied_products_spec.rb index 71119396541..a920082e711 100644 --- a/engines/dfc_provider/spec/requests/supplied_products_spec.rb +++ b/engines/dfc_provider/spec/requests/supplied_products_spec.rb @@ -44,14 +44,7 @@ parameter name: :supplied_product, in: :body, schema: { example: { - '@context': { - 'dfc-b': "http://static.datafoodconsortium.org/ontologies/DFC_BusinessOntology.owl#", - 'dfc-m': "http://static.datafoodconsortium.org/data/measures.rdf#", - 'dfc-pt': "http://static.datafoodconsortium.org/data/productTypes.rdf#", - 'dfc-b:hasUnit': { - '@type': "@id" - }, - }, + '@context': "https://www.datafoodconsortium.org", '@id': "http://test.host/api/dfc/enterprises/6201/supplied_products/0", '@type': "dfc-b:SuppliedProduct", 'dfc-b:name': "Apple", diff --git a/swagger/dfc.yaml b/swagger/dfc.yaml index fc70f05a7a4..b592fa742ea 100644 --- a/swagger/dfc.yaml +++ b/swagger/dfc.yaml @@ -553,12 +553,7 @@ paths: application/json: schema: example: - "@context": - dfc-b: http://static.datafoodconsortium.org/ontologies/DFC_BusinessOntology.owl# - dfc-m: http://static.datafoodconsortium.org/data/measures.rdf# - dfc-pt: http://static.datafoodconsortium.org/data/productTypes.rdf# - dfc-b:hasUnit: - "@type": "@id" + "@context": https://www.datafoodconsortium.org "@id": http://test.host/api/dfc/enterprises/6201/supplied_products/0 "@type": dfc-b:SuppliedProduct dfc-b:name: Apple From 96a010033de9542418229031e0b3e958ea5c000a Mon Sep 17 00:00:00 2001 From: Gaetan Craig-Riou Date: Mon, 15 Jan 2024 11:55:08 +1100 Subject: [PATCH 23/24] Fix admin view to use dfc_id This was missed in a previous refactor --- app/controllers/spree/admin/taxons_controller.rb | 2 +- app/views/spree/admin/taxons/_form.html.haml | 6 +++--- config/locales/en.yml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/controllers/spree/admin/taxons_controller.rb b/app/controllers/spree/admin/taxons_controller.rb index 2cc60bbd7eb..9efc97ed793 100644 --- a/app/controllers/spree/admin/taxons_controller.rb +++ b/app/controllers/spree/admin/taxons_controller.rb @@ -118,7 +118,7 @@ def destroy def taxon_params params.require(:taxon).permit( :name, :parent_id, :position, :icon, :description, :permalink, :taxonomy_id, - :meta_description, :meta_keywords, :meta_title, :dfc_name + :meta_description, :meta_keywords, :meta_title, :dfc_id ) end end diff --git a/app/views/spree/admin/taxons/_form.html.haml b/app/views/spree/admin/taxons/_form.html.haml index df23dde53ae..0d1bcd0605a 100644 --- a/app/views/spree/admin/taxons/_form.html.haml +++ b/app/views/spree/admin/taxons/_form.html.haml @@ -24,10 +24,10 @@ = f.label :meta_keywords, t(".meta_keywords") %br/ = f.text_field :meta_keywords, class: 'fullwidth', rows: 6 - = f.field_container :dfc_name do - = f.label :dfc_name, t(".dfc_name") + = f.field_container :dfc_id do + = f.label :dfc_id, t(".dfc_id") %br/ - = f.text_field :dfc_name, class: 'fullwidth', rows: 6 + = f.text_field :dfc_id, class: 'fullwidth', rows: 6 .omega.seven.columns = f.field_container :description do = f.label :description, t(".description") diff --git a/config/locales/en.yml b/config/locales/en.yml index 2f37ca4b5c3..d9d16c50f8a 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -4487,7 +4487,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using meta_description: Meta Description meta_keywords: Meta Keywords description: Description - dfc_name: DFC name + dfc_id: DFC URI general_settings: edit: legal_settings: "Legal Settings" From 36b3b3c989569e64cd903239c25a35885ff14845 Mon Sep 17 00:00:00 2001 From: Gaetan Craig-Riou Date: Mon, 22 Jan 2024 11:40:50 +1100 Subject: [PATCH 24/24] Switch to comparing product type `semanticId` Currently it's not possibel to compare two `DataFoodConsortium::Connector::SKOSConcept` or two `VirtualAssembly::Semantizer::SemanticObject with` `==`. Related to : https://github.com/assemblee-virtuelle/semantizer-ruby/pull/2/files --- .../spec/services/dfc_product_type_factory_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engines/dfc_provider/spec/services/dfc_product_type_factory_spec.rb b/engines/dfc_provider/spec/services/dfc_product_type_factory_spec.rb index 31096f4f9c1..be4fa4342b5 100644 --- a/engines/dfc_provider/spec/services/dfc_product_type_factory_spec.rb +++ b/engines/dfc_provider/spec/services/dfc_product_type_factory_spec.rb @@ -11,7 +11,7 @@ it "assigns a top level product type" do drink = DfcLoader.connector.PRODUCT_TYPES.DRINK - expect(described_class.for(dfc_id)).to eq drink + expect(described_class.for(dfc_id).semanticId).to eq drink.semanticId end context "with second level product type" do @@ -22,7 +22,7 @@ it "assigns a second level product type" do soft_drink = DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK - expect(described_class.for(dfc_id)).to eq soft_drink + expect(described_class.for(dfc_id).semanticId).to eq soft_drink.semanticId end end @@ -34,7 +34,7 @@ it "assigns a leaf level product type" do lemonade = DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK.LEMONADE - expect(described_class.for(dfc_id)).to eq lemonade + expect(described_class.for(dfc_id).semanticId).to eq lemonade.semanticId end end