diff --git a/engines/dfc_provider/lib/data_food_consortium/connector/connector.rb b/engines/dfc_provider/lib/data_food_consortium/connector/connector.rb index 2d657a33bc..7c1d22e6e3 100644 --- a/engines/dfc_provider/lib/data_food_consortium/connector/connector.rb +++ b/engines/dfc_provider/lib/data_food_consortium/connector/connector.rb @@ -6,6 +6,9 @@ require "datafoodconsortium/connector" # Then our tools for monky-patching: require_relative "importer" require_relative "context" +require_relative "skos_parser_element" +require_relative "skos_concept" +require_relative "skos_parser" module DataFoodConsortium module Connector diff --git a/engines/dfc_provider/lib/data_food_consortium/connector/skos_concept.rb b/engines/dfc_provider/lib/data_food_consortium/connector/skos_concept.rb new file mode 100644 index 0000000000..5672e12e6b --- /dev/null +++ b/engines/dfc_provider/lib/data_food_consortium/connector/skos_concept.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require_relative 'skos_helper' + +module DataFoodConsortium + module Connector + class SKOSConcept + include DataFoodConsortium::Connector::SKOSHelper + end + end +end diff --git a/engines/dfc_provider/lib/data_food_consortium/connector/skos_helper.rb b/engines/dfc_provider/lib/data_food_consortium/connector/skos_helper.rb new file mode 100644 index 0000000000..1d4a7e456c --- /dev/null +++ b/engines/dfc_provider/lib/data_food_consortium/connector/skos_helper.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module DataFoodConsortium::Connector::SKOSHelper + def addAttribute(name, value) + self.instance_variable_set("@#{name}", value) + self.define_singleton_method(name) do + instance_variable_get("@#{name}") + end + end + + def hasAttribute(name) + self.methods.include?(:"#{name}") + end +end 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 2cda5b9553..bdb6b23911 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 @@ -1,11 +1,75 @@ # frozen_string_literal: true +require_relative 'skos_helper' + +module DataFoodConsortium + module Connector + class SKOSInstance + include DataFoodConsortium::Connector::SKOSHelper + + # Return a list of singelton methods, ie the list of Concept available + def topConcepts + self.methods(false).sort + end + end + end +end + # Overriding the current implementation to store all parsed concepts for # lookup later. Otherwise the importer can't associate these. # This is just a workaround and needs to be solved upstream. module DataFoodConsortium module Connector class SKOSParser + CONCEPT_SCHEMES = ["Facet", "productTypes"].freeze + + def initialize + @results = DataFoodConsortium::Connector::SKOSInstance.new + @skosConcepts = {} + @rootElements = [] + @broaders = {} + # Flag used to tell the parser to use SkosConcept object when parsing data from Concept Scheme + # defined in CONCEPT_SCHEMES + @useSkosConcept = false + end + + def parse(data) + init + + data.each do |element| + current = DataFoodConsortium::Connector::SKOSParserElement.new(element) + + setSkosConceptFlag(current) + + if current.isConcept? || current.isCollection? + if !@skosConcepts.has_key?(current.id) + concept = createSKOSConcept(current) + @skosConcepts[current.id] = concept + end + + if current.hasBroader + current.broader.each do |broaderId| + if !@broaders.has_key?(broaderId) + @broaders[broaderId] = [] + end + + @broaders[broaderId].push(current.id) + end + # No broader, save the concept to the root + else + @rootElements.push(current.id) + end + end + end + + @rootElements.each do |rootElementId| + setResults(@results, rootElementId) + end + + @results + end + + # TODO check if this is still needed def self.concepts @concepts ||= {} end @@ -13,10 +77,59 @@ module DataFoodConsortium protected def createSKOSConcept(element) # rubocop:disable Naming/MethodName - concept = DataFoodConsortium::Connector::SKOSConcept.new(element.id) - concept.semanticType = element.type - self.class.concepts[element.id] = concept - concept + skosConcept = DataFoodConsortium::Connector::SKOSConcept.new( + element.id, broaders: element.broader, narrowers: element.narrower + ) + skosConcept.semanticType = element.type + # TODO check if this is still needed + # original patch by Maikel + self.class.concepts[element.id] = skosConcept + + skosConcept + end + + private + + def init + @results = DataFoodConsortium::Connector::SKOSInstance.new + @skosConcepts = {} + @rootElements = [] + @broaders = {} + @useSkosConcept = false + end + + def setResults(parent, id) + name = getValueWithoutPrefix(id) + + if !parent.hasAttribute(name) + if @useSkosConcept && !@skosConcepts[id].nil? + parent.addAttribute(name, @skosConcepts[id]) + else + parent.addAttribute(name, DataFoodConsortium::Connector::SKOSInstance.new) + end + end + + # Leaf concepts, stop the process + if !@broaders.has_key?(id) + parent.instance_variable_set("@#{name}", @skosConcepts[id]) + return + end + + @broaders[id].each do |narrower| + parentSkosInstance = parent.instance_variable_get("@#{name}") + + setResults(parentSkosInstance, narrower) # recursive call + end + end + + def setSkosConceptFlag(current) + @useSkosConcept = true if current.isConceptScheme? && matchingConceptSchemes(current) + end + + def matchingConceptSchemes(current) + regex = /#{CONCEPT_SCHEMES.join("|")}/ + + current.id =~ regex end end end diff --git a/engines/dfc_provider/lib/data_food_consortium/connector/skos_parser_element.rb b/engines/dfc_provider/lib/data_food_consortium/connector/skos_parser_element.rb new file mode 100644 index 0000000000..9328833c0d --- /dev/null +++ b/engines/dfc_provider/lib/data_food_consortium/connector/skos_parser_element.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +module DataFoodConsortium + module Connector + class SKOSParserElement + attr_reader :narrower + + def initialize(element) + @broader = [] + @narrower = [] + + if element + @id = element["@id"] + + if element["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"] + @type = extractId(element["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"]) + elsif element["@type"] + @type = extractId(element["@type"]) + else + @type = "undefined" + end + + if element["http://www.w3.org/2004/02/skos/core#broader"] + element["http://www.w3.org/2004/02/skos/core#broader"].each do |broader| + @broader.push(broader["@id"]) + end + end + + if element["http://www.w3.org/2004/02/skos/core#narrower"] + element["http://www.w3.org/2004/02/skos/core#narrower"].each do |narrower| + @narrower.push(narrower["@id"]) + end + end + else + @id = "" + @type = "" + end + end + + def isConceptScheme? + @type == "http://www.w3.org/2004/02/skos/core#ConceptScheme" + end + end + end +end