qa 4.0.0 → 4.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/app/controllers/qa/linked_data_terms_controller.rb +30 -9
- data/app/controllers/qa/terms_controller.rb +3 -2
- data/app/models/qa/linked_data/config/context_property_map.rb +6 -25
- data/app/services/qa/iri_template_service.rb +32 -24
- data/app/services/qa/linked_data/authority_service.rb +8 -0
- data/app/services/qa/linked_data/authority_url_service.rb +27 -8
- data/app/services/qa/linked_data/deep_sort_service.rb +3 -2
- data/app/services/qa/linked_data/graph_service.rb +13 -0
- data/app/services/qa/linked_data/language_service.rb +12 -0
- data/app/services/qa/linked_data/language_sort_service.rb +7 -2
- data/app/services/qa/linked_data/ldpath_service.rb +40 -0
- data/app/services/qa/linked_data/mapper/graph_ldpath_mapper_service.rb +49 -0
- data/app/services/qa/linked_data/mapper/graph_mapper_service.rb +3 -11
- data/app/services/qa/linked_data/mapper/graph_predicate_mapper_service.rb +40 -0
- data/app/services/qa/linked_data/mapper/search_results_mapper_service.rb +58 -11
- data/app/services/qa/linked_data/mapper/term_results_mapper_service.rb +80 -0
- data/config/authorities/linked_data/loc.json +13 -7
- data/config/authorities/linked_data/oclc_fast.json +13 -8
- data/lib/generators/qa/discogs/USAGE +10 -0
- data/lib/generators/qa/discogs/discogs_generator.rb +12 -0
- data/lib/generators/qa/discogs/templates/config/discogs-formats.yml +346 -0
- data/lib/generators/qa/discogs/templates/config/discogs-genres.yml +627 -0
- data/lib/generators/qa/install/templates/config/initializers/qa.rb +4 -0
- data/lib/qa.rb +6 -0
- data/lib/qa/authorities.rb +2 -0
- data/lib/qa/authorities/discogs.rb +28 -0
- data/lib/qa/authorities/discogs/discogs_instance_builder.rb +145 -0
- data/lib/qa/authorities/discogs/discogs_translation.rb +126 -0
- data/lib/qa/authorities/discogs/discogs_utils.rb +89 -0
- data/lib/qa/authorities/discogs/discogs_works_builder.rb +153 -0
- data/lib/qa/authorities/discogs/generic_authority.rb +151 -0
- data/lib/qa/authorities/discogs_subauthority.rb +9 -0
- data/lib/qa/authorities/linked_data/config.rb +7 -3
- data/lib/qa/authorities/linked_data/config/search_config.rb +99 -11
- data/lib/qa/authorities/linked_data/config/term_config.rb +112 -8
- data/lib/qa/authorities/linked_data/find_term.rb +154 -84
- data/lib/qa/authorities/linked_data/search_query.rb +76 -13
- data/lib/qa/configuration.rb +8 -0
- data/lib/qa/version.rb +1 -1
- data/spec/controllers/linked_data_terms_controller_spec.rb +151 -30
- data/spec/controllers/terms_controller_spec.rb +4 -0
- data/spec/features/linked_data/language_spec.rb +298 -0
- data/spec/fixtures/authorities/linked_data/lod_full_config.json +21 -5
- data/spec/fixtures/authorities/linked_data/lod_lang_defaults.json +4 -4
- data/spec/fixtures/authorities/linked_data/lod_lang_multi_defaults.json +4 -4
- data/spec/fixtures/authorities/linked_data/lod_lang_no_defaults.json +4 -5
- data/spec/fixtures/authorities/linked_data/lod_lang_param.json +4 -4
- data/spec/fixtures/authorities/linked_data/lod_term_uri_param_config.json +1 -1
- data/spec/fixtures/discogs-find-response-json.json +1 -0
- data/spec/fixtures/discogs-find-response-jsonld-master.json +1 -0
- data/spec/fixtures/discogs-find-response-jsonld-release.json +1 -0
- data/spec/fixtures/discogs-id-matches-master.json +1 -0
- data/spec/fixtures/discogs-id-matches-release.json +1 -0
- data/spec/fixtures/discogs-id-not-found-master.json +1 -0
- data/spec/fixtures/discogs-id-not-found-release.json +1 -0
- data/spec/fixtures/discogs-search-response-no-auth.json +1 -0
- data/spec/fixtures/discogs-search-response-no-subauth.json +1 -0
- data/spec/fixtures/discogs-search-response-subauth.json +1 -0
- data/spec/fixtures/lod_lang_search_enesfrde.rdf.xml +60 -0
- data/spec/fixtures/lod_lang_search_sv.rdf.xml +42 -0
- data/spec/fixtures/lod_loc_term_found.rdf.xml +5 -0
- data/spec/lib/authorities/discogs/generic_authority_spec.rb +235 -0
- data/spec/lib/authorities/discogs_spec.rb +17 -0
- data/spec/lib/authorities/linked_data/config_spec.rb +68 -5
- data/spec/lib/authorities/linked_data/find_term_spec.rb +298 -3
- data/spec/lib/authorities/linked_data/generic_authority_spec.rb +46 -485
- data/spec/lib/authorities/linked_data/search_config_spec.rb +154 -3
- data/spec/lib/authorities/linked_data/search_query_spec.rb +240 -3
- data/spec/lib/authorities/linked_data/term_config_spec.rb +193 -5
- data/spec/lib/configuration_spec.rb +18 -0
- data/spec/models/linked_data/config/context_property_map_spec.rb +3 -31
- data/spec/services/iri_template_service_spec.rb +54 -12
- data/spec/{lib/authorities → services}/linked_data/authority_service_spec.rb +47 -0
- data/spec/services/linked_data/language_service_spec.rb +52 -11
- data/spec/services/linked_data/ldpath_service_spec.rb +61 -0
- data/spec/services/linked_data/mapper/graph_ldpath_mapper_service_spec.rb +118 -0
- data/spec/services/linked_data/mapper/graph_predicate_mapper_service_spec.rb +110 -0
- data/spec/services/linked_data/mapper/term_results_mapper_service_spec.rb +94 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/support/matchers/include_hash.rb +5 -0
- data/spec/test_app_templates/lib/generators/test_app_generator.rb +4 -0
- metadata +73 -5
- data/lib/qa/authorities/linked_data/rdf_helper.rb +0 -49
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f8ae75788063293cff50f32548b9eccbd6d0c837
|
4
|
+
data.tar.gz: 3aad18f6f86d31d4e8a13941407c8890d1389a21
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4c1bd3ffc5d4d6d663fa656cebc9400f390a393862c3d68c454866f42f50f803236f64cd97dc3c0a63e5e895e2ca575b2f32b5d9e5372c6fe572bf08b1484397
|
7
|
+
data.tar.gz: 761ef1f50ecdfdc425a8deb49b3672508955725aa3339514c517b8bbc0cd6d1285b689adde5d35e2e3aa877e659ead79492c6ad6d82de345bd6c3cb230245f93
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Code:
|
4
4
|
[![Gem Version](https://badge.fury.io/rb/qa.png)](http://badge.fury.io/rb/qa)
|
5
|
-
[![Build Status](https://
|
5
|
+
[![Build Status](https://circleci.com/gh/samvera/questioning_authority.svg?style=svg)](https://circleci.com/gh/samvera/questioning_authority)
|
6
6
|
[![Coverage Status](https://coveralls.io/repos/github/samvera/questioning_authority/badge.svg?branch=master)](https://coveralls.io/github/samvera/questioning_authority?branch=master)
|
7
7
|
|
8
8
|
Docs:
|
@@ -16,11 +16,12 @@ class Qa::LinkedDataTermsController < ::ApplicationController
|
|
16
16
|
head :not_found
|
17
17
|
end
|
18
18
|
|
19
|
-
# Return a list of supported authority names
|
20
|
-
# get "/list/linked_data/authorities"
|
19
|
+
# Return a list of supported authority names optionally with details about the authority
|
20
|
+
# get "/list/linked_data/authorities?details=true|false" (default details=false)
|
21
21
|
# @see Qa::LinkedData::AuthorityService#authority_names
|
22
|
+
# @see Qa::LinkedData::AuthorityService#authority_details
|
22
23
|
def list
|
23
|
-
|
24
|
+
details? ? render_detail_list : render_simple_list
|
24
25
|
end
|
25
26
|
|
26
27
|
# Reload authority configurations
|
@@ -34,8 +35,8 @@ class Qa::LinkedDataTermsController < ::ApplicationController
|
|
34
35
|
# Return a list of terms based on a query
|
35
36
|
# get "/search/linked_data/:vocab(/:subauthority)"
|
36
37
|
# @see Qa::Authorities::LinkedData::SearchQuery#search
|
37
|
-
def search # rubocop:disable Metrics/MethodLength
|
38
|
-
terms = @authority.search(query, subauth: subauthority, language: language, replacements: replacement_params, context: context?)
|
38
|
+
def search # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
39
|
+
terms = @authority.search(query, subauth: subauthority, language: language, replacements: replacement_params, context: context?, performance_data: performance_data?)
|
39
40
|
cors_allow_origin_header(response)
|
40
41
|
render json: terms
|
41
42
|
rescue Qa::ServiceUnavailable
|
@@ -58,7 +59,7 @@ class Qa::LinkedDataTermsController < ::ApplicationController
|
|
58
59
|
# get "/show/linked_data/:vocab/:subauthority/:id
|
59
60
|
# @see Qa::Authorities::LinkedData::FindTerm#find
|
60
61
|
def show # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
61
|
-
term = @authority.find(id, subauth: subauthority, language: language, replacements: replacement_params, jsonld: jsonld?)
|
62
|
+
term = @authority.find(id, subauth: subauthority, language: language, replacements: replacement_params, jsonld: jsonld?, performance_data: performance_data?)
|
62
63
|
cors_allow_origin_header(response)
|
63
64
|
content_type = jsonld? ? 'application/ld+json' : 'application/json'
|
64
65
|
render json: term, content_type: content_type
|
@@ -85,7 +86,7 @@ class Qa::LinkedDataTermsController < ::ApplicationController
|
|
85
86
|
# get "/fetch/linked_data/:vocab"
|
86
87
|
# @see Qa::Authorities::LinkedData::FindTerm#find
|
87
88
|
def fetch # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
88
|
-
term = @authority.find(uri, subauth: subauthority, language: language, replacements: replacement_params, jsonld: jsonld?)
|
89
|
+
term = @authority.find(uri, subauth: subauthority, language: language, replacements: replacement_params, jsonld: jsonld?, performance_data: performance_data?)
|
89
90
|
cors_allow_origin_header(response)
|
90
91
|
content_type = jsonld? ? 'application/ld+json' : 'application/json'
|
91
92
|
render json: term, content_type: content_type
|
@@ -110,6 +111,14 @@ class Qa::LinkedDataTermsController < ::ApplicationController
|
|
110
111
|
|
111
112
|
private
|
112
113
|
|
114
|
+
def render_simple_list
|
115
|
+
render json: Qa::LinkedData::AuthorityService.authority_names.to_json
|
116
|
+
end
|
117
|
+
|
118
|
+
def render_detail_list
|
119
|
+
render json: Qa::LinkedData::AuthorityService.authority_details.to_json
|
120
|
+
end
|
121
|
+
|
113
122
|
def check_authority
|
114
123
|
if params[:vocab].nil? || !params[:vocab].size.positive? # rubocop:disable Style/GuardClause
|
115
124
|
msg = "Required param 'vocab' is missing or empty"
|
@@ -180,7 +189,9 @@ class Qa::LinkedDataTermsController < ::ApplicationController
|
|
180
189
|
end
|
181
190
|
|
182
191
|
def language
|
183
|
-
|
192
|
+
request_language = request.env['HTTP_ACCEPT_LANGUAGE']
|
193
|
+
request_language = request_language.scan(/^[a-z]{2}/).first if request_language.present?
|
194
|
+
params[:lang] || request_language
|
184
195
|
end
|
185
196
|
|
186
197
|
def subauthority
|
@@ -188,7 +199,7 @@ class Qa::LinkedDataTermsController < ::ApplicationController
|
|
188
199
|
end
|
189
200
|
|
190
201
|
def replacement_params
|
191
|
-
params.reject { |k, _v| ['q', 'vocab', 'controller', 'action', 'subauthority', '
|
202
|
+
params.reject { |k, _v| ['q', 'vocab', 'controller', 'action', 'subauthority', 'lang', 'id'].include?(k) }
|
192
203
|
end
|
193
204
|
|
194
205
|
def subauth_warn_msg
|
@@ -210,6 +221,16 @@ class Qa::LinkedDataTermsController < ::ApplicationController
|
|
210
221
|
context.casecmp('true').zero?
|
211
222
|
end
|
212
223
|
|
224
|
+
def details?
|
225
|
+
details = params.fetch(:details, 'false')
|
226
|
+
details.casecmp('true').zero?
|
227
|
+
end
|
228
|
+
|
229
|
+
def performance_data?
|
230
|
+
performance_data = params.fetch(:performance_data, 'false')
|
231
|
+
performance_data.casecmp('true').zero?
|
232
|
+
end
|
233
|
+
|
213
234
|
def validate_auth_reload_token
|
214
235
|
token = params.key?(:auth_token) ? params[:auth_token] : nil
|
215
236
|
valid = Qa.config.valid_authority_reload_token?(token)
|
@@ -28,9 +28,10 @@ class Qa::TermsController < ::ApplicationController
|
|
28
28
|
|
29
29
|
# If the subauthority supports it, return all the information for a given term
|
30
30
|
def show
|
31
|
-
term = @authority.find(params[:id])
|
31
|
+
term = @authority.method(:find).arity == 2 ? @authority.find(params[:id], self) : @authority.find(params[:id])
|
32
32
|
cors_allow_origin_header(response)
|
33
|
-
|
33
|
+
content_type = params["format"] == "jsonld" ? 'application/ld+json' : 'application/json'
|
34
|
+
render json: term, content_type: content_type
|
34
35
|
end
|
35
36
|
|
36
37
|
def check_vocab_param
|
@@ -67,7 +67,7 @@ module Qa
|
|
67
67
|
# Values of this property for a specfic subject URI
|
68
68
|
# @return [Array<String>] values for this property
|
69
69
|
def values(graph, subject_uri)
|
70
|
-
ldpath_evaluate(basic_program, graph, subject_uri)
|
70
|
+
Qa::LinkedData::LdpathService.ldpath_evaluate(program: basic_program, graph: graph, subject_uri: subject_uri)
|
71
71
|
end
|
72
72
|
|
73
73
|
# Values of this property for a specfic subject URI with URI values expanded to include id and label.
|
@@ -98,44 +98,25 @@ module Qa
|
|
98
98
|
end
|
99
99
|
|
100
100
|
def basic_program
|
101
|
-
@basic_program ||= ldpath_program(ldpath)
|
101
|
+
@basic_program ||= Qa::LinkedData::LdpathService.ldpath_program(ldpath: ldpath, prefixes: prefixes)
|
102
102
|
end
|
103
103
|
|
104
104
|
def expansion_label_program
|
105
|
-
@expansion_label_program ||= ldpath_program(expansion_label_ldpath)
|
105
|
+
@expansion_label_program ||= Qa::LinkedData::LdpathService.ldpath_program(ldpath: expansion_label_ldpath, prefixes: prefixes)
|
106
106
|
end
|
107
107
|
|
108
108
|
def expansion_id_program
|
109
|
-
@expansion_id_program ||= ldpath_program(expansion_id_ldpath)
|
110
|
-
end
|
111
|
-
|
112
|
-
def ldpath_program(ldpath)
|
113
|
-
program_code = ""
|
114
|
-
prefixes.each { |key, url| program_code << "@prefix #{key} : <#{url}> \;\n" }
|
115
|
-
program_code << "property = #{ldpath} \;"
|
116
|
-
Ldpath::Program.parse program_code
|
117
|
-
rescue => e
|
118
|
-
Rails.logger.warn("WARNING: #{I18n.t('qa.linked_data.ldpath.parse_logger_error')} (ldpath='#{ldpath}')\n cause: #{e.message}")
|
119
|
-
raise StandardError, I18n.t('qa.linked_data.ldpath.parse_error')
|
120
|
-
end
|
121
|
-
|
122
|
-
def ldpath_evaluate(program, graph, subject_uri)
|
123
|
-
return VALUE_ON_ERROR if program.blank?
|
124
|
-
output = program.evaluate subject_uri, graph
|
125
|
-
output.present? ? output['property'].uniq : nil
|
126
|
-
rescue => e
|
127
|
-
Rails.logger.warn("WARNING: #{I18n.t('qa.linked_data.ldpath.evaluate_logger_error')} (ldpath='#{ldpath}')\n cause: #{e.message}")
|
128
|
-
raise StandardError, I18n.t('qa.linked_data.ldpath.evaluate_error')
|
109
|
+
@expansion_id_program ||= Qa::LinkedData::LdpathService.ldpath_program(ldpath: expansion_id_ldpath, prefixes: prefixes)
|
129
110
|
end
|
130
111
|
|
131
112
|
def expansion_label(graph, uri)
|
132
|
-
label = ldpath_evaluate(expansion_label_program, graph, RDF::URI(uri))
|
113
|
+
label = Qa::LinkedData::LdpathService.ldpath_evaluate(program: expansion_label_program, graph: graph, subject_uri: RDF::URI(uri))
|
133
114
|
label.size == 1 ? label.first : label
|
134
115
|
end
|
135
116
|
|
136
117
|
def expansion_id(graph, uri)
|
137
118
|
return uri if expansion_id_ldpath.blank?
|
138
|
-
id = ldpath_evaluate(expansion_id_program, graph, RDF::URI(uri))
|
119
|
+
id = Qa::LinkedData::LdpathService.ldpath_evaluate(program: expansion_id_program, graph: graph, subject_uri: RDF::URI(uri))
|
139
120
|
id.size == 1 ? id.first : id
|
140
121
|
end
|
141
122
|
end
|
@@ -1,31 +1,39 @@
|
|
1
1
|
# Provide service for building a URL based on an IRI Templated Link and its variable mappings based on provided substitutions.
|
2
2
|
module Qa
|
3
3
|
class IriTemplateService
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
4
|
+
class << self
|
5
|
+
# Construct an url from an IriTemplate making identified substitutions
|
6
|
+
# @param url_config [Qa::IriTemplate::UrlConfig] configuration (json) holding the template and variable mappings
|
7
|
+
# @param substitutions [HashWithIndifferentAccess] name-value pairs to substitute into the url template
|
8
|
+
# @return [String] url with substitutions
|
9
|
+
def build_url(url_config:, substitutions:)
|
10
|
+
# TODO: This is a very simple approach using direct substitution into the template string.
|
11
|
+
# Better would be to...
|
12
|
+
# * appropriately adds '?' or '&'
|
13
|
+
# * ensure proper escaping of values (e.g. value="A simple string" which is encoded as A%20simple%20string)
|
14
|
+
# Even more advanced would be to...
|
15
|
+
# * support BasicRepresentation (which is what it does now)
|
16
|
+
# * support ExplicitRepresentation
|
17
|
+
# * literal encoding for values (e.g. value="A simple string" becomes %22A%20simple%20string%22)
|
18
|
+
# * language encoding for values (e.g. value="A simple string" becomes value="A simple string"@en which is encoded as %22A%20simple%20string%22%40en)
|
19
|
+
# * type encoding for values (e.g. value=5.5 becomes value="5.5"^^http://www.w3.org/2001/XMLSchema#decimal which is encoded
|
20
|
+
# as %225.5%22%5E%5Ehttp%3A%2F%2Fwww.w3.org%2F2001%2FXMLSchema%23decimal)
|
21
|
+
# Fuller implementations parse the template into component parts and then build the URL by adding parts in as applicable.
|
22
|
+
url = url_config.template
|
23
|
+
url_config.mapping.each do |m|
|
24
|
+
key = m.variable
|
25
|
+
url = url.gsub("{#{key}}", m.simple_value(substitutions[key]))
|
26
|
+
url = url.gsub("{?#{key}}", m.parameter_value(substitutions[key]))
|
27
|
+
end
|
28
|
+
clean(url)
|
27
29
|
end
|
28
|
-
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# In the process of substitution, if a value is missing, you can end up with '?&', '&&', or ending with '&'. These are all removed this method.
|
34
|
+
def clean(url)
|
35
|
+
url.gsub(/&&*/, '&').chomp('&').gsub('?&', '?')
|
36
|
+
end
|
29
37
|
end
|
30
38
|
end
|
31
39
|
end
|
@@ -41,6 +41,14 @@ module Qa
|
|
41
41
|
def self.authority_names
|
42
42
|
authority_configs.keys.sort
|
43
43
|
end
|
44
|
+
|
45
|
+
# Get the list of names and details of the loaded authorities
|
46
|
+
# @return [Array<String>] names of the authority config files that are currently loaded
|
47
|
+
def self.authority_details
|
48
|
+
details = []
|
49
|
+
authority_names.each { |auth_name| details << Qa::Authorities::LinkedData::Config.new(auth_name).authority_info }
|
50
|
+
details.flatten
|
51
|
+
end
|
44
52
|
end
|
45
53
|
end
|
46
54
|
end
|
@@ -4,16 +4,17 @@ module Qa
|
|
4
4
|
class AuthorityUrlService
|
5
5
|
class << self
|
6
6
|
# Build a url for an authority/subauthority for the specified action.
|
7
|
-
# @param
|
8
|
-
# @param subauthority [String] name of a subauthority
|
7
|
+
# @param action_config [Qa::Authorities::LinkedData::SearchConfig | Qa::Authorities::LinkedData::TermConfig] action configuration for the authority
|
9
8
|
# @param action [Symbol] action with valid values :search or :term
|
10
9
|
# @param action_request [String] the request the user is making of the authority (e.g. query text or term id/uri)
|
11
|
-
# @param substitutions [Hash] variable-value pairs to substitute into the URL template
|
12
|
-
# @
|
13
|
-
|
10
|
+
# @param substitutions [Hash] variable-value pairs to substitute into the URL template (optional)
|
11
|
+
# @param subauthority [String] name of a subauthority (optional)
|
12
|
+
# @param language [Array<Symbol>] languages for filtering returned literals (optional)
|
13
|
+
# @return a valid URL that submits the action request to the external authority
|
14
|
+
def build_url(action_config:, action:, action_request:, substitutions: {}, subauthority: nil, language: nil) # rubocop:disable Metrics/ParameterLists
|
14
15
|
action_validation(action)
|
15
16
|
url_config = action_config.url_config
|
16
|
-
selected_substitutions = url_config.extract_substitutions(combined_substitutions(action_config, action, action_request, substitutions, subauthority))
|
17
|
+
selected_substitutions = url_config.extract_substitutions(combined_substitutions(action_config, action, action_request, substitutions, subauthority, language))
|
17
18
|
Qa::IriTemplateService.build_url(url_config: url_config, substitutions: selected_substitutions)
|
18
19
|
end
|
19
20
|
|
@@ -24,9 +25,10 @@ module Qa
|
|
24
25
|
raise Qa::UnsupportedAction, "#{action} Not Supported - Action must be one of the supported actions (e.g. :term, :search)"
|
25
26
|
end
|
26
27
|
|
27
|
-
def combined_substitutions(action_config, action, action_request, substitutions, subauthority)
|
28
|
+
def combined_substitutions(action_config, action, action_request, substitutions, subauthority, language) # rubocop:disable Metrics/ParameterLists
|
28
29
|
substitutions[action_request_variable(action_config, action)] = action_request
|
29
|
-
substitutions[action_subauth_variable(action_config)] = action_subauth_variable_value(action_config, subauthority) if subauthority.present?
|
30
|
+
substitutions[action_subauth_variable(action_config)] = action_subauth_variable_value(action_config, subauthority) if supports_subauthorities?(action_config) && subauthority.present?
|
31
|
+
substitutions[action_language_variable(action_config)] = language_value(language) if supports_language_parameter?(action_config) && language.present?
|
30
32
|
substitutions
|
31
33
|
end
|
32
34
|
|
@@ -35,6 +37,10 @@ module Qa
|
|
35
37
|
action_config.qa_replacement_patterns[key]
|
36
38
|
end
|
37
39
|
|
40
|
+
def supports_subauthorities?(action_config)
|
41
|
+
action_config.supports_subauthorities?
|
42
|
+
end
|
43
|
+
|
38
44
|
def action_subauth_variable(action_config)
|
39
45
|
action_config.qa_replacement_patterns[:subauth]
|
40
46
|
end
|
@@ -42,6 +48,19 @@ module Qa
|
|
42
48
|
def action_subauth_variable_value(action_config, subauthority)
|
43
49
|
action_config.subauthorities[subauthority.to_sym]
|
44
50
|
end
|
51
|
+
|
52
|
+
def supports_language_parameter?(action_config)
|
53
|
+
action_config.supports_language_parameter?
|
54
|
+
end
|
55
|
+
|
56
|
+
def action_language_variable(action_config)
|
57
|
+
action_config.qa_replacement_patterns[:lang]
|
58
|
+
end
|
59
|
+
|
60
|
+
def language_value(language)
|
61
|
+
return nil if language.blank?
|
62
|
+
language.first
|
63
|
+
end
|
45
64
|
end
|
46
65
|
end
|
47
66
|
end
|
@@ -2,8 +2,9 @@
|
|
2
2
|
module Qa
|
3
3
|
module LinkedData
|
4
4
|
class DeepSortService
|
5
|
-
# @
|
6
|
-
# @
|
5
|
+
# @param [Array<Hash<Symbol,Array<RDF::Literal>>>] the array of hashes to sort
|
6
|
+
# @param [sort_key] the key in the hash on whose value the array will be sorted
|
7
|
+
# @param [Symbol] preferred language to appear first in the list; defaults to no preference
|
7
8
|
# @return instance of this class
|
8
9
|
# @example the_array parameter
|
9
10
|
# [
|
@@ -41,6 +41,19 @@ module Qa
|
|
41
41
|
values
|
42
42
|
end
|
43
43
|
|
44
|
+
# Get subjects that have the object value for the predicate in the graph.
|
45
|
+
# @param graph [RDF::Graph] the graph to search
|
46
|
+
# @param predicate [RDF::URI] the URI of the predicate
|
47
|
+
# @param object_value [String] the object value
|
48
|
+
# @return [Array] all subjects for the predicate-object_value pair
|
49
|
+
def subjects_for_object_value(graph:, predicate:, object_value:)
|
50
|
+
subjects = []
|
51
|
+
graph.query([:subject, predicate, object_value]) do |statement|
|
52
|
+
subjects << statement.subject
|
53
|
+
end
|
54
|
+
subjects
|
55
|
+
end
|
56
|
+
|
44
57
|
def deep_copy(graph:)
|
45
58
|
new_graph = RDF::Graph.new
|
46
59
|
graph.statements.each do |st|
|
@@ -2,13 +2,24 @@
|
|
2
2
|
module Qa
|
3
3
|
module LinkedData
|
4
4
|
class LanguageService
|
5
|
+
WILDCARD = '*'.freeze
|
6
|
+
|
5
7
|
class << self
|
8
|
+
# @param user_langauge [Symbol|String] the language code (e.g. :en, :fr) specified as URL parameter or on URL header
|
9
|
+
# @param authority_language [Symbol|String|Array<Symbol|String>] the default language specified in the authority's configuration file
|
10
|
+
# @return [Array<Symbol>] the selected language(s) normalized as an array of symbols (e.g. [:en, :fr])
|
11
|
+
# @note Precedence from high to low:
|
12
|
+
# * user_language (with URL parameter preferred over URL header)
|
13
|
+
# * authority_language (defined in authority config)
|
14
|
+
# * site default_language (defined in QA initializer)
|
6
15
|
def preferred_language(user_language: nil, authority_language: nil)
|
7
16
|
return normalize_language(user_language) if user_language.present?
|
8
17
|
return normalize_language(authority_language) if authority_language.present?
|
9
18
|
normalize_language(Qa.config.default_language)
|
10
19
|
end
|
11
20
|
|
21
|
+
# @param [RDF::Literal] the literal to check
|
22
|
+
# @return [Boolean] true if literal has a language tag; otherwise, false
|
12
23
|
def literal_has_language_marker?(literal)
|
13
24
|
return false unless literal.respond_to?(:language)
|
14
25
|
literal.language.present?
|
@@ -22,6 +33,7 @@ module Qa
|
|
22
33
|
def normalize_language(language)
|
23
34
|
return language if language.blank?
|
24
35
|
language = [language] unless language.is_a? Array
|
36
|
+
return nil if language.include?(WILDCARD)
|
25
37
|
language.map(&:to_sym)
|
26
38
|
end
|
27
39
|
end
|
@@ -7,7 +7,6 @@ module Qa
|
|
7
7
|
attr_reader :literals, :preferred_language
|
8
8
|
attr_reader :languages, :bins
|
9
9
|
private :literals, :preferred_language, :languages, :bins
|
10
|
-
# private :literals, :preferred_language, :languages, :languages=, :bins, :bins=
|
11
10
|
|
12
11
|
# @param [Array<RDF::Literals>] string literals to sort
|
13
12
|
# @param [Symbol] preferred language to appear first in the list; defaults to no preference
|
@@ -20,7 +19,7 @@ module Qa
|
|
20
19
|
end
|
21
20
|
|
22
21
|
# Sort the literals stored in this instance of the service
|
23
|
-
# @return sorted version of literals
|
22
|
+
# @return [Array<RDF::Literals] sorted version of literals
|
24
23
|
def sort
|
25
24
|
return literals unless literals.present?
|
26
25
|
return @sorted_literals if @sorted_literals.present?
|
@@ -30,6 +29,12 @@ module Qa
|
|
30
29
|
@sorted_literals = construct_sorted_literals
|
31
30
|
end
|
32
31
|
|
32
|
+
# Sort the literals and return as an array of strings with only unique literals and empty strings removed
|
33
|
+
# @return [Array<String>] sorted version of literals as strings
|
34
|
+
def uniq_sorted_strings
|
35
|
+
sort.map(&:to_s).uniq.delete_if(&:blank?)
|
36
|
+
end
|
37
|
+
|
33
38
|
private
|
34
39
|
|
35
40
|
def construct_sorted_literals
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# Defines the external authority predicates used to extract additional context from the graph.
|
2
|
+
require 'ldpath'
|
3
|
+
|
4
|
+
module Qa
|
5
|
+
module LinkedData
|
6
|
+
class LdpathService
|
7
|
+
VALUE_ON_ERROR = [].freeze
|
8
|
+
|
9
|
+
# Create the ldpath program for a given ldpath.
|
10
|
+
# @param ldpath [String] ldpath to follow to get a value from a graph (documation: http://marmotta.apache.org/ldpath/language.html)
|
11
|
+
# @param prefixes [Hash] shortcut names for URI prefixes with key = part of predicate that is the same for all terms (e.g. { "madsrdf": "http://www.loc.gov/mads/rdf/v1#" })
|
12
|
+
# @return [Ldpath::Program] an executable program that will extract a value from a graph
|
13
|
+
def self.ldpath_program(ldpath:, prefixes: {})
|
14
|
+
program_code = ""
|
15
|
+
prefixes.each { |key, url| program_code << "@prefix #{key} : <#{url}> \;\n" }
|
16
|
+
program_code << "property = #{ldpath} \;"
|
17
|
+
Ldpath::Program.parse program_code
|
18
|
+
rescue => e
|
19
|
+
Rails.logger.warn("WARNING: #{I18n.t('qa.linked_data.ldpath.parse_logger_error')}... cause: #{e.message}\n ldpath_program=\n#{program_code}")
|
20
|
+
raise StandardError, I18n.t("qa.linked_data.ldpath.parse_error") + "... cause: #{e.message}"
|
21
|
+
end
|
22
|
+
|
23
|
+
# Evaluate an ldpath for a specific subject uri in the context of a graph and return the extracted values.
|
24
|
+
# @param program [Ldpath::Program] an executable program that will extract a value from a graph
|
25
|
+
# @param graph [RDF::Graph] the graph from which the values will be extracted
|
26
|
+
# @param subject_uri [RDF::URI] retrieved values will be limited to those with the subject uri
|
27
|
+
# @param limit_to_context [Boolean] if true, the evaluation process will not make any outside network calls.
|
28
|
+
# It will limit results to those found in the context graph.
|
29
|
+
## @return [Array<String>] the extracted values based on the ldpath
|
30
|
+
def self.ldpath_evaluate(program:, graph:, subject_uri:, limit_to_context: Qa.config.limit_ldpath_to_context?)
|
31
|
+
return VALUE_ON_ERROR if program.blank?
|
32
|
+
output = program.evaluate(subject_uri, context: graph, limit_to_context: limit_to_context)
|
33
|
+
output.present? ? output['property'].uniq : nil
|
34
|
+
rescue => e
|
35
|
+
Rails.logger.warn("WARNING: #{I18n.t('qa.linked_data.ldpath.evaluate_logger_error')} (cause: #{e.message}")
|
36
|
+
raise StandardError, I18n.t("qa.linked_data.ldpath.evaluate_error") + "... cause: #{e.message}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|