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 a23a55be0f..37143af832 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 4289cc584c..d3bb4d2960 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 b586866110..2cc60bbd7e 100644 --- a/app/controllers/spree/admin/taxons_controller.rb +++ b/app/controllers/spree/admin/taxons_controller.rb @@ -117,8 +117,8 @@ module Spree 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 37143af832..df23dde53a 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 d3bb4d2960..2f37ca4b5c 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 0000000000..224880511e --- /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 91083ef7e9..e7bb67f638 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -881,6 +881,7 @@ ActiveRecord::Schema[7.0].define(version: 20231003000823494) do 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 69a9871c17..d35ca518ac 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 @@ class SuppliedProductBuilder < DfcBuilder 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 @@ class SuppliedProductBuilder < DfcBuilder end end + # TODO fix the taxon here def self.import_product(supplied_product) Spree::Product.new( name: supplied_product.name, @@ -62,4 +57,17 @@ class SuppliedProductBuilder < DfcBuilder 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 51563b23e7..77fa2ae9e7 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 @@ describe SuppliedProductBuilder do 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 @@ describe SuppliedProductBuilder do 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 vegetable + 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 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 8f39d971b7..8c8728bd11 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 @@ describe "SuppliedProducts", type: :request, swagger_doc: "dfc.yaml", rswag_auto 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 @@ describe "SuppliedProducts", type: :request, swagger_doc: "dfc.yaml", rswag_auto 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 d35ca518ac..b1360b9443 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 @@ class SuppliedProductBuilder < DfcBuilder 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 search.nil? + return nil if PRODUCT_TYPES[taxon_name.to_sym].nil? - DfcLoader.connector.PRODUCT_TYPES.public_send(root_product_types[search]) + 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 + + type = call_dfc_product_type(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 + 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 77fa2ae9e7..40ba5cc3e2 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 @@ describe SuppliedProductBuilder do 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 @@ describe SuppliedProductBuilder do 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 8c8728bd11..4211fb0552 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 @@ describe "SuppliedProducts", type: :request, swagger_doc: "dfc.yaml", rswag_auto ) } 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 @@ describe "SuppliedProducts", type: :request, swagger_doc: "dfc.yaml", rswag_auto 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 c082630044..367e92d0b4 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 @@ describe "CatalogItems", type: :request, swagger_doc: "dfc.yaml", :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 e043610dd8..1b7e7da393 100644 --- a/engines/dfc_provider/spec/requests/enterprises_spec.rb +++ b/engines/dfc_provider/spec/requests/enterprises_spec.rb @@ -29,6 +29,7 @@ describe "Enterprises", type: :request, swagger_doc: "dfc.yaml", rswag_autodoc: :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 6751e2ec05..9db45ed575 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 dda1422ac2..3dc4675e35 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 @@ module DataFoodConsortium 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 4e7d2b289a..354fe7bf2a 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 @@ require_relative "skos_parser" module DataFoodConsortium module Connector - class Importer + class Importer # rubocop:disable Metrics/ClassLength TYPES = [ DataFoodConsortium::Connector::CatalogItem, DataFoodConsortium::Connector::Enterprise, @@ -106,7 +106,7 @@ module DataFoodConsortium 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 @@ module DataFoodConsortium 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 @@ module DataFoodConsortium "#{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 7da3fdd88b..e292468818 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 @@ describe DataFoodConsortium::Connector::Importer do name: "Tomato", description: "Awesome tomato", totalTheoreticalStock: 3, + productType: non_local_vegetable, ) end let(:product_data) do @@ -36,7 +37,8 @@ describe DataFoodConsortium::Connector::Importer do "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 @@ describe DataFoodConsortium::Connector::Importer do "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 @@ describe DataFoodConsortium::Connector::Importer do "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 @@ describe DataFoodConsortium::Connector::Importer do 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 @@ describe DataFoodConsortium::Connector::Importer do 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 @@ describe DataFoodConsortium::Connector::Importer do 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 @@ describe DataFoodConsortium::Connector::Importer do 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 @@ describe DataFoodConsortium::Connector::Importer do 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 @@ describe DataFoodConsortium::Connector::Importer do 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 @@ describe DataFoodConsortium::Connector::Importer do 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 b1360b9443..3814e98212 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 @@ class SuppliedProductBuilder < DfcBuilder 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 367e92d0b4..27249778de 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 @@ describe "CatalogItems", type: :request, swagger_doc: "dfc.yaml", :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 1b7e7da393..5a604ff463 100644 --- a/engines/dfc_provider/spec/requests/enterprises_spec.rb +++ b/engines/dfc_provider/spec/requests/enterprises_spec.rb @@ -29,7 +29,7 @@ describe "Enterprises", type: :request, swagger_doc: "dfc.yaml", rswag_autodoc: :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 4211fb0552..219a0e4692 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 @@ describe "SuppliedProducts", type: :request, swagger_doc: "dfc.yaml", rswag_auto ) } 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 40ba5cc3e2..2e213a0525 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 @@ describe SuppliedProductBuilder do 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 3814e98212..970bef97b7 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 @@ class SuppliedProductBuilder < DfcBuilder 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 @@ class SuppliedProductBuilder < DfcBuilder 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 2e213a0525..1c46bc7f13 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 @@ describe SuppliedProductBuilder do 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 219a0e4692..745d0de9ea 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 @@ describe "SuppliedProducts", type: :request, swagger_doc: "dfc.yaml", rswag_auto } 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 @@ describe "SuppliedProducts", type: :request, swagger_doc: "dfc.yaml", rswag_auto 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 970bef97b7..5596cb9593 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 @@ class SuppliedProductBuilder < DfcBuilder 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 cc081912bb..d3e69dbb67 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 745d0de9ea..d3a63e40ee 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 @@ describe "SuppliedProducts", type: :request, swagger_doc: "dfc.yaml", rswag_auto 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 @@ describe "SuppliedProducts", type: :request, swagger_doc: "dfc.yaml", rswag_auto }.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 9db45ed575..fc70f05a7a 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 224880511e..22ae971b70 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 e7bb67f638..5a78096336 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -881,7 +881,7 @@ ActiveRecord::Schema[7.0].define(version: 20231003000823494) do 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 5596cb9593..5bfa74aad5 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 @@ class SuppliedProductBuilder < DfcBuilder 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 @@ class SuppliedProductBuilder < DfcBuilder 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 @@ class SuppliedProductBuilder < DfcBuilder 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 27249778de..f047812e00 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 @@ describe "CatalogItems", type: :request, swagger_doc: "dfc.yaml", :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 5a604ff463..ca875360ae 100644 --- a/engines/dfc_provider/spec/requests/enterprises_spec.rb +++ b/engines/dfc_provider/spec/requests/enterprises_spec.rb @@ -29,7 +29,14 @@ describe "Enterprises", type: :request, swagger_doc: "dfc.yaml", rswag_autodoc: :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 d3a63e40ee..f0e3792552 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 @@ describe "SuppliedProducts", type: :request, swagger_doc: "dfc.yaml", rswag_auto ) } 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 @@ describe "SuppliedProducts", type: :request, swagger_doc: "dfc.yaml", rswag_auto 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 1c46bc7f13..977c64d256 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 @@ describe SuppliedProductBuilder do 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 @@ describe SuppliedProductBuilder do 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 @@ describe SuppliedProductBuilder do 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 @@ describe SuppliedProductBuilder do 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 @@ describe SuppliedProductBuilder do ) 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 5bfa74aad5..7d04a8413f 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 @@ class SuppliedProductBuilder < DfcBuilder 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 f0e3792552..7111939654 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 @@ describe "SuppliedProducts", type: :request, swagger_doc: "dfc.yaml", rswag_auto 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 977c64d256..770e72a95f 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 @@ describe SuppliedProductBuilder do ) 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 0000000000..07c39c06cf --- /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 7d04a8413f..ea475da1ec 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 @@ class SuppliedProductBuilder < DfcBuilder 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 @@ class SuppliedProductBuilder < DfcBuilder 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 0000000000..31096f4f9c --- /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 770e72a95f..b8e41df742 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 @@ describe SuppliedProductBuilder do 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 @@ describe SuppliedProductBuilder do 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 354fe7bf2a..9f13e68c60 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 @@ module DataFoodConsortium 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 07c39c06cf..1ffb67de84 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 @@ class DfcProductTypeFactory 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 @@ class DfcProductTypeFactory 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 1ffb67de84..f41bfe2fe1 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 @@ class DfcProductTypeFactory 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 9f13e68c60..21da91b2a7 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 @@ module DataFoodConsortium "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 3dc4675e35..dda1422ac2 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 @@ module DataFoodConsortium 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 21da91b2a7..d668facf3b 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 @@ module DataFoodConsortium 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 e292468818..465fadac45 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 @@ describe DataFoodConsortium::Connector::Importer do } 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 @@ describe DataFoodConsortium::Connector::Importer do 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 7111939654..a920082e71 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 @@ describe "SuppliedProducts", type: :request, swagger_doc: "dfc.yaml", rswag_auto 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 fc70f05a7a..b592fa742e 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 2cc60bbd7e..9efc97ed79 100644 --- a/app/controllers/spree/admin/taxons_controller.rb +++ b/app/controllers/spree/admin/taxons_controller.rb @@ -118,7 +118,7 @@ module Spree 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 df23dde53a..0d1bcd0605 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 2f37ca4b5c..d9d16c50f8 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 31096f4f9c..be4fa4342b 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 @@ describe DfcProductTypeFactory do 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 @@ describe DfcProductTypeFactory do 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 @@ describe DfcProductTypeFactory do 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