qa 4.0.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
[](http://badge.fury.io/rb/qa)
|
|
5
|
-
[](https://circleci.com/gh/samvera/questioning_authority)
|
|
6
6
|
[](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
|