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:
Gaetan Craig-Riou
2023-12-01 14:26:09 +11:00
parent afdd68269c
commit e846761e54
5 changed files with 190 additions and 4 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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