Skip to content

Commit

Permalink
Merge 82add09 into 94f720f
Browse files Browse the repository at this point in the history
  • Loading branch information
elrayle committed Dec 13, 2018
2 parents 94f720f + 82add09 commit cc4292c
Show file tree
Hide file tree
Showing 14 changed files with 322 additions and 128 deletions.
9 changes: 4 additions & 5 deletions app/services/qa/linked_data/deep_sort_service.rb
Expand Up @@ -91,9 +91,9 @@ def downcase_string(idx = 0)
to_downcase(literal(idx))
end

def language(literal = literals.first)
return literal.language if literal.respond_to?(:language)
nil
def language(lit = literals.first)
return nil unless Qa::LinkedData::LanguageService.literal_has_language_marker? lit
lit.language
end

def includes_preferred_language?
Expand Down Expand Up @@ -206,8 +206,7 @@ def only_one_has_language_marker?(lit, other_lit)
end

def language?(lit)
lang = lit.language if lit.respond_to?(:language)
lang.present?
Qa::LinkedData::LanguageService.literal_has_language_marker? lit
end

def preferred_language?(lang)
Expand Down
50 changes: 31 additions & 19 deletions app/services/qa/linked_data/graph_service.rb
Expand Up @@ -14,17 +14,16 @@ def load_graph(url:)

# Create a new graph with statements filtered out
# @param graph [RDF::Graph] the original graph to be filtered
# @param language [String | Symbol | Array<String|Symbol>] will keep any statement whose object's language matches the language filter
# (only applies to statements that respond to language) (e.g. "en" or :en or ["en", "fr"] or [:en, :fr])
# @param language [Array<Symbol>] will keep any statement whose object's language matches the language filter
# (only applies to statements that respond to language) (e.g. [:fr] or [:en, :fr])
# @param remove_blanknode_subjects [Boolean] will remove any statement whose subject is a blanknode, if true
# @return [RDF::Graph] a new instance of graph with statements not matching the filters removed
def filter(graph:, language: nil, remove_blanknode_subjects: false)
return graph unless graph.present?
return graph unless language.present? || remove_blanknode_subjects
filtered_graph = graph
language = normalize_language(language)
filtered_graph.each do |st|
filtered_graph.delete(st) if filter_out_blanknode(remove_blanknode_subjects, st.subject) || filter_out_language(language, st.object)
filtered_graph = deep_copy(graph: graph)
filtered_graph.statements.each do |st|
filtered_graph.delete(st) if filter_out_blanknode(remove_blanknode_subjects, st.subject) || filter_out_language(graph, language, st)
end
filtered_graph
end
Expand All @@ -42,17 +41,39 @@ def object_values(graph:, subject:, predicate:)
values
end

def deep_copy(graph:)
new_graph = RDF::Graph.new
graph.statements.each do |st|
new_graph.insert(st.dup)
end
end

private

def filter_out_blanknode(remove, subj)
remove && subj.anonymous?
end

def filter_out_language(language, obj)
# Filter out language based on...
# * do not remove if the object literal does not respond to :language
# * do not remove if the object literal does not have a language marker
# * do not remove if the object has the targeted language marker
# * do not remove if none of the other objects with this statement's predicate have the targeted language marker
def filter_out_language(graph, language, statement)
return false if language.blank?
return false unless obj.respond_to?(:language)
return false if obj.language.blank?
!language.include?(obj.language)
return false unless Qa::LinkedData::LanguageService.literal_has_language_marker?(statement.object)
objects = object_values(graph: graph, subject: statement.subject, predicate: statement.predicate)
return false unless at_least_one_object_has_language?(objects, language)
!language.include?(statement.object.language)
end

def at_least_one_object_has_language?(objects, language)
objects.each do |obj|
next unless Qa::LinkedData::LanguageService.literal_has_language_marker?(obj)
next unless language.include? obj.language
return true
end
false
end

def process_error(e, url)
Expand All @@ -78,15 +99,6 @@ def ioerror_code(e)
z = msg.size - 2
msg[a..z]
end

# Normalize language
# @param [String | Symbol | Array] language for filtering graph (e.g. "en" OR :en OR ["en", "fr"] OR [:en, :fr])
# @return [Array<Symbol>] an array of languages encoded as symbols (e.g. [:en] OR [:en, :fr])
def normalize_language(language)
return language if language.blank?
language = [language] unless language.is_a? Array
language.map(&:to_sym)
end
end
end
end
Expand Down
30 changes: 30 additions & 0 deletions app/services/qa/linked_data/language_service.rb
@@ -0,0 +1,30 @@
# Service to determine which language to use for sorting and filtering.
module Qa
module LinkedData
class LanguageService
class << self
def preferred_language(user_language: nil, authority_language: nil)
return normalize_language(user_language) if user_language.present?
return normalize_language(authority_language) if authority_language.present?
normalize_language(Qa.config.default_language)
end

def literal_has_language_marker?(literal)
return false unless literal.respond_to?(:language)
literal.language.present?
end

private

# Normalize language
# @param [String | Symbol | Array] language for filtering graph (e.g. "en" OR :en OR ["en", "fr"] OR [:en, :fr])
# @return [Array<Symbol>] an array of languages encoded as symbols (e.g. [:en] OR [:en, :fr])
def normalize_language(language)
return language if language.blank?
language = [language] unless language.is_a? Array
language.map(&:to_sym)
end
end
end
end
end
4 changes: 2 additions & 2 deletions app/services/qa/linked_data/language_sort_service.rb
Expand Up @@ -39,8 +39,8 @@ def construct_sorted_literals
end

def language(literal)
language = literal.language if literal.respond_to?(:language)
language.present? ? language : LANGUAGE_LOCALE_KEY_FOR_NO_LANGUAGE
return LANGUAGE_LOCALE_KEY_FOR_NO_LANGUAGE unless Qa::LinkedData::LanguageService.literal_has_language_marker? literal
literal.language
end

def move_no_language_to_end
Expand Down
4 changes: 4 additions & 0 deletions lib/generators/qa/install/templates/config/initializers/qa.rb
Expand Up @@ -10,4 +10,8 @@
# requiring a restart of rails. By default, reloading through the browser is not allowed
# when the token is nil or blank. Change to any string to control who has access to reload.
# config.authorized_reload_token = 'YOUR_AUTH_TOKEN_DEFINED_HERE'

# For linked data access, specify default language for sorting and selection. The default is only used if a language is not
# specified in the authority's configuration file and not passed in as a parameter. (e.g. :en, [:en], or [:en, :fr])
# config.default_language = :en
end
24 changes: 14 additions & 10 deletions lib/qa/authorities/linked_data/find_term.rb
Expand Up @@ -10,7 +10,8 @@ def initialize(term_config)
@term_config = term_config
end

attr_reader :term_config, :graph
attr_reader :term_config, :full_graph, :filtered_graph, :language
private :full_graph, :filtered_graph, :language

delegate :term_subauthority?, to: :term_config

Expand All @@ -35,24 +36,27 @@ def initialize(term_config)
# "http://schema.org/sameAs":["http://id.loc.gov/authorities/names/n79021621","https://viaf.org/viaf/126293486"] } }
def find(id, language: nil, replacements: {}, subauth: nil, jsonld: false)
raise Qa::InvalidLinkedDataAuthority, "Unable to initialize linked data term sub-authority #{subauth}" unless subauth.nil? || term_subauthority?(subauth)
language ||= term_config.term_language
@language = Qa::LinkedData::LanguageService.preferred_language(user_language: language, authority_language: term_config.term_language)
url = Qa::LinkedData::AuthorityUrlService.build_url(action_config: term_config, action: :term, action_request: id, substitutions: replacements, subauthority: subauth)
Rails.logger.info "QA Linked Data term url: #{url}"
load_graph(url: url, language: language)
return "{}" unless graph.size.positive?
return graph.dump(:jsonld, standard_prefixes: true) if jsonld
load_graph(url: url)
return "{}" unless full_graph.size.positive?
return full_graph.dump(:jsonld, standard_prefixes: true) if jsonld
parse_term_authority_response(id)
end

private

def load_graph(url:, language:)
@graph = Qa::LinkedData::GraphService.load_graph(url: url)
@graph = Qa::LinkedData::GraphService.filter(graph: @graph, language: language) unless language.blank?
def load_graph(url:)
# @graph = Qa::LinkedData::GraphService.load_graph(url: url)
@full_graph = Qa::LinkedData::GraphService.load_graph(url: url)
return unless @full_graph.size.positive?
@filtered_graph = Qa::LinkedData::GraphService.deep_copy(graph: @full_graph)
@filtered_graph = Qa::LinkedData::GraphService.filter(graph: @filtered_graph, language: language) unless language.blank?
end

def parse_term_authority_response(id)
results = extract_preds(graph, preds_for_term)
results = extract_preds(filtered_graph, preds_for_term)
consolidated_results = consolidate_term_results(results)
json_results = convert_term_to_json(consolidated_results)
termhash = select_json_result_for_id(json_results, id)
Expand Down Expand Up @@ -130,7 +134,7 @@ def select_json_result_for_id(json_results, id)

def predicates_with_subject_uri(expected_uri) # rubocop:disable Metrics/MethodLength
predicates_hash = {}
graph.statements.each do |st|
@full_graph.statements.each do |st|
subj = st.subject.to_s
next unless subj == expected_uri
pred = st.predicate.to_s
Expand Down
5 changes: 2 additions & 3 deletions lib/qa/authorities/linked_data/search_query.rb
Expand Up @@ -11,7 +11,7 @@ def initialize(search_config)
end

attr_reader :search_config, :graph, :language
private :language
private :graph, :language

delegate :subauthority?, :supports_sort?, to: :search_config

Expand All @@ -27,8 +27,7 @@ def initialize(search_config)
# {"uri":"http://id.worldcat.org/fast/409667","id":"409667","label":"Cornell, Ezra, 1807-1874"} ]
def search(query, language: nil, replacements: {}, subauth: nil)
raise Qa::InvalidLinkedDataAuthority, "Unable to initialize linked data search sub-authority #{subauth}" unless subauth.nil? || subauthority?(subauth)
language ||= search_config.language
@language = language
@language = Qa::LinkedData::LanguageService.preferred_language(user_language: language, authority_language: search_config.language)
url = Qa::LinkedData::AuthorityUrlService.build_url(action_config: search_config, action: :search, action_request: query, substitutions: replacements, subauthority: subauth)
Rails.logger.info "QA Linked Data search url: #{url}"
load_graph(url: url)
Expand Down
7 changes: 7 additions & 0 deletions lib/qa/configuration.rb
Expand Up @@ -30,5 +30,12 @@ def valid_authority_reload_token?(token)

# Hold linked data authority configs
attr_accessor :linked_data_authority_configs

# For linked data access, specify default language for sorting and selection. The default is only used if a language is not
# specified in the authority's configuration file and not passed in as a parameter. (e.g. :en, [:en], or [:en, :fr])
attr_writer :default_language
def default_language
@default_language ||= :en
end
end
end
4 changes: 2 additions & 2 deletions spec/fixtures/authorities/linked_data/lod_lang_defaults.json
Expand Up @@ -18,7 +18,7 @@
"term_id": "term_id"
},
"term_id": "ID",
"language": [ "en" ],
"language": [ "fr" ],
"results": {
"id_predicate": "http://id.loc.gov/vocabulary/identifiers/lccn",
"label_predicate": "http://www.w3.org/2004/02/skos/core#prefLabel",
Expand All @@ -43,7 +43,7 @@
"qa_replacement_patterns": {
"query": "query"
},
"language": [ "en" ],
"language": [ "fr" ],
"results": {
"id_predicate": "http://purl.org/dc/terms/identifier",
"label_predicate": "http://www.w3.org/2004/02/skos/core#prefLabel",
Expand Down
11 changes: 11 additions & 0 deletions spec/fixtures/lod_lang_search_filtering.nt
@@ -0,0 +1,11 @@
<http://id.worldcat.org/fast/530369> <http://purl.org/dc/terms/identifier> "530369" .
<http://id.worldcat.org/fast/530369> <http://www.w3.org/2004/02/skos/core#prefLabel> "buttermilk"@en
<http://id.worldcat.org/fast/530369> <http://www.w3.org/2004/02/skos/core#prefLabel> "Babeurre"@fr
<http://id.worldcat.org/fast/530369> <http://www.w3.org/2004/02/skos/core#prefLabel> "Buttermilch"
<http://id.worldcat.org/fast/5140> <http://purl.org/dc/terms/identifier> "5140" .
<http://id.worldcat.org/fast/5140> <http://www.w3.org/2004/02/skos/core#prefLabel> "dried milk"@en
<http://id.worldcat.org/fast/5140> <http://www.w3.org/2004/02/skos/core#prefLabel> "lait en poudre"@fr
<http://id.worldcat.org/fast/5140> <http://www.w3.org/2004/02/skos/core#prefLabel> "getrocknete Milch"@de
<http://id.worldcat.org/fast/557490> <http://purl.org/dc/terms/identifier> "557490" .
<http://id.worldcat.org/fast/557490> <http://www.w3.org/2004/02/skos/core#prefLabel> "condensed milk"@en
<http://id.worldcat.org/fast/557490> <http://www.w3.org/2004/02/skos/core#prefLabel> "Kondensmilch"@de

0 comments on commit cc4292c

Please sign in to comment.