mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-03-31 06:31:41 +00:00
WIP Patch DFC connector to fix SKOS Concept parsing
This is to let us use intermediary SKOS Concept. Upstream PR for more details: https://github.com/datafoodconsortium/connector-codegen/pull/10/files
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user