qa 3.1.0 → 4.0.0.rc1
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 +5 -5
- data/README.md +16 -548
- data/app/controllers/qa/linked_data_terms_controller.rb +64 -42
- data/app/controllers/qa/terms_controller.rb +14 -6
- data/app/models/qa/iri_template/url_config.rb +47 -0
- data/app/models/qa/iri_template/variable_map.rb +62 -0
- data/app/models/qa/linked_data/config/context_map.rb +77 -0
- data/app/models/qa/linked_data/config/context_property_map.rb +144 -0
- data/app/models/qa/linked_data/config/helper.rb +34 -0
- data/app/services/qa/iri_template_service.rb +31 -0
- data/{lib/qa/authorities → app/services/qa}/linked_data/authority_service.rb +3 -4
- data/app/services/qa/linked_data/authority_url_service.rb +48 -0
- data/app/services/qa/linked_data/deep_sort_service.rb +238 -0
- data/app/services/qa/linked_data/graph_service.rb +106 -0
- data/app/services/qa/linked_data/language_service.rb +30 -0
- data/app/services/qa/linked_data/language_sort_service.rb +81 -0
- data/app/services/qa/linked_data/mapper/context_mapper_service.rb +59 -0
- data/app/services/qa/linked_data/mapper/graph_mapper_service.rb +40 -0
- data/app/services/qa/linked_data/mapper/search_results_mapper_service.rb +70 -0
- data/config/authorities/linked_data/loc.json +5 -2
- data/config/authorities/linked_data/oclc_fast.json +3 -2
- data/config/initializers/linked_data_authorities.rb +1 -1
- data/config/locales/qa.en.yml +9 -0
- data/lib/generators/qa/install/templates/config/initializers/qa.rb +4 -0
- data/lib/qa.rb +8 -0
- data/lib/qa/authorities/assign_fast/generic_authority.rb +1 -1
- data/lib/qa/authorities/base.rb +0 -11
- data/lib/qa/authorities/crossref/generic_authority.rb +1 -1
- data/lib/qa/authorities/geonames.rb +1 -1
- data/lib/qa/authorities/getty/aat.rb +7 -2
- data/lib/qa/authorities/getty/tgn.rb +7 -2
- data/lib/qa/authorities/getty/ulan.rb +7 -2
- data/lib/qa/authorities/linked_data.rb +0 -1
- data/lib/qa/authorities/linked_data/config.rb +29 -28
- data/lib/qa/authorities/linked_data/config/search_config.rb +21 -79
- data/lib/qa/authorities/linked_data/config/term_config.rb +7 -77
- data/lib/qa/authorities/linked_data/find_term.rb +25 -17
- data/lib/qa/authorities/linked_data/generic_authority.rb +6 -5
- data/lib/qa/authorities/linked_data/rdf_helper.rb +6 -73
- data/lib/qa/authorities/linked_data/search_query.rb +54 -101
- data/lib/qa/authorities/loc/generic_authority.rb +4 -4
- data/lib/qa/authorities/web_service_base.rb +1 -8
- data/lib/qa/configuration.rb +7 -0
- data/lib/qa/version.rb +1 -1
- data/lib/tasks/mesh.rake +19 -18
- data/spec/controllers/linked_data_terms_controller_spec.rb +51 -1
- data/spec/controllers/terms_controller_spec.rb +15 -15
- data/spec/fixtures/authorities/linked_data/lod_encoding_config.json +2 -1
- data/spec/fixtures/authorities/linked_data/lod_full_config.json +56 -2
- data/spec/fixtures/authorities/linked_data/lod_full_config_1_0.json +164 -0
- data/spec/fixtures/authorities/linked_data/lod_lang_defaults.json +5 -4
- data/spec/fixtures/authorities/linked_data/lod_lang_multi_defaults.json +3 -2
- data/spec/fixtures/authorities/linked_data/lod_lang_no_defaults.json +3 -2
- data/spec/fixtures/authorities/linked_data/lod_lang_param.json +3 -2
- data/spec/fixtures/authorities/linked_data/lod_min_config.json +3 -2
- data/spec/fixtures/authorities/linked_data/lod_search_only_config.json +2 -1
- data/spec/fixtures/authorities/linked_data/lod_sort.json +2 -1
- data/spec/fixtures/authorities/linked_data/lod_term_id_param_config.json +2 -1
- data/spec/fixtures/authorities/linked_data/lod_term_only_config.json +2 -1
- data/spec/fixtures/authorities/linked_data/lod_term_uri_param_config.json +2 -1
- data/spec/fixtures/getty-error-response.txt +10 -0
- data/spec/fixtures/lod_2_ranked_2_unranked.nt +17 -0
- data/spec/fixtures/lod_3_ranked_varying_preds.nt +16 -0
- data/spec/fixtures/lod_lang_search_filtering.nt +11 -0
- data/spec/fixtures/lod_search_with_blanknode_subjects.nt +18 -0
- data/spec/fixtures/lod_term_with_blanknode_objects.nt +8 -0
- data/spec/lib/authorities/assign_fast_spec.rb +1 -0
- data/spec/lib/authorities/getty/aat_spec.rb +14 -2
- data/spec/lib/authorities/getty/tgn_spec.rb +14 -2
- data/spec/lib/authorities/getty/ulan_spec.rb +14 -2
- data/spec/lib/authorities/linked_data/authority_service_spec.rb +2 -1
- data/spec/lib/authorities/linked_data/config_spec.rb +284 -5
- data/spec/lib/authorities/linked_data/find_term_spec.rb +3 -1
- data/spec/lib/authorities/linked_data/generic_authority_spec.rb +92 -42
- data/spec/lib/authorities/linked_data/search_config_spec.rb +67 -160
- data/spec/lib/authorities/linked_data/search_query_spec.rb +3 -127
- data/spec/lib/authorities/linked_data/term_config_spec.rb +6 -134
- data/spec/lib/authorities/loc_spec.rb +9 -9
- data/spec/lib/configuration_spec.rb +20 -7
- data/spec/lib/tasks/mesh.rake_spec.rb +2 -2
- data/spec/models/iri_template/url_config_spec.rb +102 -0
- data/spec/models/iri_template/variable_map_spec.rb +105 -0
- data/spec/models/linked_data/config/context_map_spec.rb +148 -0
- data/spec/models/linked_data/config/context_property_map_spec.rb +286 -0
- data/spec/services/iri_template_service_spec.rb +69 -0
- data/spec/services/linked_data/authority_url_service_spec.rb +107 -0
- data/spec/services/linked_data/deep_sort_service_spec.rb +260 -0
- data/spec/services/linked_data/graph_service_spec.rb +232 -0
- data/spec/services/linked_data/language_service_spec.rb +66 -0
- data/spec/services/linked_data/language_sort_service_spec.rb +58 -0
- data/spec/services/linked_data/mapper/context_mapper_service_spec.rb +137 -0
- data/spec/services/linked_data/mapper/graph_mapper_service_spec.rb +110 -0
- data/spec/services/linked_data/mapper/search_results_mapper_service_spec.rb +109 -0
- data/spec/spec_helper.rb +10 -2
- metadata +81 -11
|
@@ -18,36 +18,39 @@ class Qa::LinkedDataTermsController < ::ApplicationController
|
|
|
18
18
|
|
|
19
19
|
# Return a list of supported authority names
|
|
20
20
|
# get "/list/linked_data/authorities"
|
|
21
|
-
# @see Qa::
|
|
21
|
+
# @see Qa::LinkedData::AuthorityService#authority_names
|
|
22
22
|
def list
|
|
23
|
-
render json: Qa::
|
|
23
|
+
render json: Qa::LinkedData::AuthorityService.authority_names.to_json
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
# Reload authority configurations
|
|
27
27
|
# get "/reload/linked_data/authorities?auth_token=YOUR_AUTH_TOKEN_DEFINED_HERE"
|
|
28
|
-
# @see Qa::
|
|
28
|
+
# @see Qa::LinkedData::AuthorityService#load_authorities
|
|
29
29
|
def reload
|
|
30
|
-
Qa::
|
|
30
|
+
Qa::LinkedData::AuthorityService.load_authorities
|
|
31
31
|
list
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
# Return a list of terms based on a query
|
|
35
35
|
# get "/search/linked_data/:vocab(/:subauthority)"
|
|
36
36
|
# @see Qa::Authorities::LinkedData::SearchQuery#search
|
|
37
|
-
def search
|
|
38
|
-
terms = @authority.search(query, subauth: subauthority, language: language, replacements: replacement_params)
|
|
37
|
+
def search # rubocop:disable Metrics/MethodLength
|
|
38
|
+
terms = @authority.search(query, subauth: subauthority, language: language, replacements: replacement_params, context: context?)
|
|
39
39
|
cors_allow_origin_header(response)
|
|
40
40
|
render json: terms
|
|
41
41
|
rescue Qa::ServiceUnavailable
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
msg = "Service Unavailable - Search query #{query} unsuccessful for#{subauth_warn_msg} authority #{vocab_param}"
|
|
43
|
+
logger.warn msg
|
|
44
|
+
render json: { errors: msg }, status: :service_unavailable
|
|
44
45
|
rescue Qa::ServiceError
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
msg = "Internal Server Error - Search query #{query} unsuccessful for#{subauth_warn_msg} authority #{vocab_param}"
|
|
47
|
+
logger.warn msg
|
|
48
|
+
render json: { errors: msg }, status: :internal_server_error
|
|
47
49
|
rescue RDF::FormatError
|
|
48
|
-
|
|
50
|
+
msg = "RDF Format Error - Results from search query #{query} for#{subauth_warn_msg} authority #{vocab_param} " \
|
|
49
51
|
"was not identified as a valid RDF format. You may need to include the linkeddata gem."
|
|
50
|
-
|
|
52
|
+
logger.warn msg
|
|
53
|
+
render json: { errors: msg }, status: :internal_server_error
|
|
51
54
|
end
|
|
52
55
|
|
|
53
56
|
# Return all the information for a given term given an id or URI
|
|
@@ -60,18 +63,22 @@ class Qa::LinkedDataTermsController < ::ApplicationController
|
|
|
60
63
|
content_type = jsonld? ? 'application/ld+json' : 'application/json'
|
|
61
64
|
render json: term, content_type: content_type
|
|
62
65
|
rescue Qa::TermNotFound
|
|
63
|
-
|
|
64
|
-
|
|
66
|
+
msg = "Term Not Found - Fetch term #{id} unsuccessful for#{subauth_warn_msg} authority #{vocab_param}"
|
|
67
|
+
logger.warn msg
|
|
68
|
+
render json: { errors: msg }, status: :not_found
|
|
65
69
|
rescue Qa::ServiceUnavailable
|
|
66
|
-
|
|
67
|
-
|
|
70
|
+
msg = "Service Unavailable - Fetch term #{id} unsuccessful for#{subauth_warn_msg} authority #{vocab_param}"
|
|
71
|
+
logger.warn msg
|
|
72
|
+
render json: { errors: msg }, status: :service_unavailable
|
|
68
73
|
rescue Qa::ServiceError
|
|
69
|
-
|
|
70
|
-
|
|
74
|
+
msg = "Internal Server Error - Fetch term #{id} unsuccessful for#{subauth_warn_msg} authority #{vocab_param}"
|
|
75
|
+
logger.warn msg
|
|
76
|
+
render json: { errors: msg }, status: :internal_server_error
|
|
71
77
|
rescue RDF::FormatError
|
|
72
|
-
|
|
78
|
+
msg = "RDF Format Error - Results from fetch term #{id} for#{subauth_warn_msg} authority #{vocab_param} " \
|
|
73
79
|
"was not identified as a valid RDF format. You may need to include the linkeddata gem."
|
|
74
|
-
|
|
80
|
+
logger.warn msg
|
|
81
|
+
render json: { errors: msg }, status: :internal_server_error
|
|
75
82
|
end
|
|
76
83
|
|
|
77
84
|
# Return all the information for a given term given a URI
|
|
@@ -83,50 +90,58 @@ class Qa::LinkedDataTermsController < ::ApplicationController
|
|
|
83
90
|
content_type = jsonld? ? 'application/ld+json' : 'application/json'
|
|
84
91
|
render json: term, content_type: content_type
|
|
85
92
|
rescue Qa::TermNotFound
|
|
86
|
-
|
|
87
|
-
|
|
93
|
+
msg = "Term Not Found - Fetch term #{uri} unsuccessful for#{subauth_warn_msg} authority #{vocab_param}"
|
|
94
|
+
logger.warn msg
|
|
95
|
+
render json: { errors: msg }, status: :not_found
|
|
88
96
|
rescue Qa::ServiceUnavailable
|
|
89
|
-
|
|
90
|
-
|
|
97
|
+
msg = "Service Unavailable - Fetch term #{uri} unsuccessful for#{subauth_warn_msg} authority #{vocab_param}"
|
|
98
|
+
logger.warn msg
|
|
99
|
+
render json: { errors: msg }, status: :service_unavailable
|
|
91
100
|
rescue Qa::ServiceError
|
|
92
|
-
|
|
93
|
-
|
|
101
|
+
msg = "Internal Server Error - Fetch term #{uri} unsuccessful for#{subauth_warn_msg} authority #{vocab_param}"
|
|
102
|
+
logger.warn msg
|
|
103
|
+
render json: { errors: msg }, status: :internal_server_error
|
|
94
104
|
rescue RDF::FormatError
|
|
95
|
-
|
|
105
|
+
msg = "RDF Format Error - Results from fetch term #{uri} for#{subauth_warn_msg} authority #{vocab_param} " \
|
|
96
106
|
"was not identified as a valid RDF format. You may need to include the linkeddata gem."
|
|
97
|
-
|
|
107
|
+
logger.warn msg
|
|
108
|
+
render json: { errors: msg }, status: :internal_server_error
|
|
98
109
|
end
|
|
99
110
|
|
|
100
111
|
private
|
|
101
112
|
|
|
102
113
|
def check_authority
|
|
103
114
|
if params[:vocab].nil? || !params[:vocab].size.positive? # rubocop:disable Style/GuardClause
|
|
104
|
-
|
|
105
|
-
|
|
115
|
+
msg = "Required param 'vocab' is missing or empty"
|
|
116
|
+
logger.warn msg
|
|
117
|
+
render json: { errors: msg }, status: :bad_request
|
|
106
118
|
end
|
|
107
119
|
end
|
|
108
120
|
|
|
109
121
|
def check_search_subauthority
|
|
110
122
|
return if subauthority.nil?
|
|
111
123
|
unless @authority.search_subauthority?(subauthority) # rubocop:disable Style/GuardClause
|
|
112
|
-
|
|
113
|
-
|
|
124
|
+
msg = "Unable to initialize linked data search sub-authority '#{subauthority}' for authority '#{vocab_param}'"
|
|
125
|
+
logger.warn msg
|
|
126
|
+
render json: { errors: msg }, status: :bad_request
|
|
114
127
|
end
|
|
115
128
|
end
|
|
116
129
|
|
|
117
130
|
def check_show_subauthority
|
|
118
131
|
return if subauthority.nil?
|
|
119
132
|
unless @authority.term_subauthority?(subauthority) # rubocop:disable Style/GuardClause
|
|
120
|
-
|
|
121
|
-
|
|
133
|
+
msg = "Unable to initialize linked data term sub-authority '#{subauthority}' for authority '#{vocab_param}'"
|
|
134
|
+
logger.warn msg
|
|
135
|
+
render json: { errors: msg }, status: :bad_request
|
|
122
136
|
end
|
|
123
137
|
end
|
|
124
138
|
|
|
125
139
|
def init_authority
|
|
126
140
|
@authority = Qa::Authorities::LinkedData::GenericAuthority.new(vocab_param)
|
|
127
141
|
rescue Qa::InvalidLinkedDataAuthority => e
|
|
128
|
-
|
|
129
|
-
|
|
142
|
+
msg = e.message
|
|
143
|
+
logger.warn msg
|
|
144
|
+
render json: { errors: msg }, status: :bad_request
|
|
130
145
|
end
|
|
131
146
|
|
|
132
147
|
def vocab_param
|
|
@@ -146,8 +161,9 @@ class Qa::LinkedDataTermsController < ::ApplicationController
|
|
|
146
161
|
end
|
|
147
162
|
|
|
148
163
|
def missing_required_param(action_name, param_name)
|
|
149
|
-
|
|
150
|
-
|
|
164
|
+
msg = "Required #{action_name} param '#{param_name}' is missing or empty"
|
|
165
|
+
logger.warn msg
|
|
166
|
+
render json: { errors: msg }, status: :bad_request
|
|
151
167
|
end
|
|
152
168
|
|
|
153
169
|
# converts wildcards into URL-encoded characters
|
|
@@ -176,7 +192,7 @@ class Qa::LinkedDataTermsController < ::ApplicationController
|
|
|
176
192
|
end
|
|
177
193
|
|
|
178
194
|
def subauth_warn_msg
|
|
179
|
-
subauthority.
|
|
195
|
+
subauthority.blank? ? "" : " sub-authority #{subauthority} in"
|
|
180
196
|
end
|
|
181
197
|
|
|
182
198
|
def format
|
|
@@ -186,15 +202,21 @@ class Qa::LinkedDataTermsController < ::ApplicationController
|
|
|
186
202
|
end
|
|
187
203
|
|
|
188
204
|
def jsonld?
|
|
189
|
-
format
|
|
205
|
+
format.casecmp('jsonld').zero?
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def context?
|
|
209
|
+
context = params.fetch(:context, 'false')
|
|
210
|
+
context.casecmp('true').zero?
|
|
190
211
|
end
|
|
191
212
|
|
|
192
213
|
def validate_auth_reload_token
|
|
193
214
|
token = params.key?(:auth_token) ? params[:auth_token] : nil
|
|
194
215
|
valid = Qa.config.valid_authority_reload_token?(token)
|
|
195
216
|
return true if valid
|
|
196
|
-
|
|
197
|
-
|
|
217
|
+
msg = "FAIL: unable to reload authorities; error_msg: Invalid token (#{token}) does not match expected token."
|
|
218
|
+
logger.warn msg
|
|
219
|
+
render json: { errors: msg }, status: :unauthorized
|
|
198
220
|
false
|
|
199
221
|
end
|
|
200
222
|
end
|
|
@@ -34,15 +34,19 @@ class Qa::TermsController < ::ApplicationController
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def check_vocab_param
|
|
37
|
-
|
|
37
|
+
return if params[:vocab].present?
|
|
38
|
+
msg = "Required param 'vocab' is missing or empty"
|
|
39
|
+
logger.warn msg
|
|
40
|
+
render json: { errors: msg }, status: :bad_request
|
|
38
41
|
end
|
|
39
42
|
|
|
40
43
|
def init_authority # rubocop:disable Metrics/MethodLength
|
|
41
44
|
begin
|
|
42
45
|
mod = authority_class.camelize.constantize
|
|
43
46
|
rescue NameError
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
msg = "Unable to initialize authority #{authority_class}"
|
|
48
|
+
logger.warn msg
|
|
49
|
+
render json: { errors: msg }, status: :bad_request
|
|
46
50
|
return
|
|
47
51
|
end
|
|
48
52
|
begin
|
|
@@ -53,13 +57,17 @@ class Qa::TermsController < ::ApplicationController
|
|
|
53
57
|
mod.subauthority_for(params[:subauthority])
|
|
54
58
|
end
|
|
55
59
|
rescue Qa::InvalidSubAuthority, Qa::MissingSubAuthority => e
|
|
56
|
-
|
|
57
|
-
|
|
60
|
+
msg = e.message
|
|
61
|
+
logger.warn msg
|
|
62
|
+
render json: { errors: msg }, status: :bad_request
|
|
58
63
|
end
|
|
59
64
|
end
|
|
60
65
|
|
|
61
66
|
def check_query_param
|
|
62
|
-
|
|
67
|
+
return if params[:q].present?
|
|
68
|
+
msg = "Required param 'q' is missing or empty"
|
|
69
|
+
logger.warn msg
|
|
70
|
+
render json: { errors: msg }, status: :bad_request
|
|
63
71
|
end
|
|
64
72
|
|
|
65
73
|
private
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Provide access to iri template configuration.
|
|
2
|
+
# See https://www.hydra-cg.com/spec/latest/core/#templated-links for information on IRI Templated Links.
|
|
3
|
+
# TODO: It would be good to find a more complete resource describing templated links.
|
|
4
|
+
module Qa
|
|
5
|
+
module IriTemplate
|
|
6
|
+
class UrlConfig
|
|
7
|
+
TYPE = "IriTemplate".freeze
|
|
8
|
+
CONTEXT = "http://www.w3.org/ns/hydra/context.jsonld".freeze
|
|
9
|
+
attr_reader :template # [String] the URL template with variables for substitution (required)
|
|
10
|
+
attr_reader :variable_representation # [String] always "BasicRepresentation" # TODO what other values are supported and what do they mean
|
|
11
|
+
attr_reader :mapping # [Array<Qa::IriTempalte::VariableMap>] array of maps for use with a template (required)
|
|
12
|
+
|
|
13
|
+
# @param [Hash] url_config configuration hash for the iri template
|
|
14
|
+
# @option url_config [String] :template the URL template with variables for substitution (required)
|
|
15
|
+
# @option url_config [String] :variable_representation always "BasicRepresentation" # TODO what other values are supported and what do they mean
|
|
16
|
+
# @option url_config [Array<Hash>] :mapping array of maps for use with a template (required)
|
|
17
|
+
def initialize(url_config)
|
|
18
|
+
@url_config = url_config
|
|
19
|
+
@template = Qa::LinkedData::Config::Helper.fetch_required(url_config, :template, nil)
|
|
20
|
+
@mapping = extract_mapping
|
|
21
|
+
@variable_representation = Qa::LinkedData::Config::Helper.fetch(url_config, :variable_representation, 'BasicRepresentation')
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Selective extract substitution variable-value pairs from the provided substitutions.
|
|
25
|
+
# @param [Hash, ActionController::Parameters] full set of passed in substitution values
|
|
26
|
+
# @return [HashWithIndifferentAccess] Only variable-value pairs for variables defined in the variable mapping.
|
|
27
|
+
def extract_substitutions(substitutions)
|
|
28
|
+
selected_substitutions = HashWithIndifferentAccess.new
|
|
29
|
+
mapping.each do |m|
|
|
30
|
+
selected_substitutions[m.variable] = substitutions[m.variable] if substitutions.key? m.variable
|
|
31
|
+
end
|
|
32
|
+
selected_substitutions
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
# Initialize the variable maps
|
|
38
|
+
# @param config [Hash] configuration holding the variable maps to be extracted
|
|
39
|
+
# @return [Array<IriTemplate::Map>] array of the variable maps
|
|
40
|
+
def extract_mapping
|
|
41
|
+
mapping = Qa::LinkedData::Config::Helper.fetch_required(@url_config, :mapping, nil)
|
|
42
|
+
raise Qa::InvalidConfiguration, "mapping must include at least one map" if mapping.empty?
|
|
43
|
+
mapping.collect { |m| Qa::IriTemplate::VariableMap.new(m) }
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Provide access to iri template variable map configuration.
|
|
2
|
+
# See https://www.hydra-cg.com/spec/latest/core/#templated-links for information on IRI Templated Links - Variable Mapping.
|
|
3
|
+
# TODO: It would be good to find a more complete resource describing templated links.
|
|
4
|
+
|
|
5
|
+
module Qa
|
|
6
|
+
module IriTemplate
|
|
7
|
+
class VariableMap
|
|
8
|
+
TYPE = "IriTemplateMapping".freeze
|
|
9
|
+
attr_reader :variable
|
|
10
|
+
attr_reader :default
|
|
11
|
+
private :default
|
|
12
|
+
|
|
13
|
+
# @param [Hash] variable_map configuration hash for the variable map
|
|
14
|
+
# @option variable_map [String] :variable (required) name of the variable in the template (e.g. {?query} has the name 'query')
|
|
15
|
+
# @option variable_map [String] :property (optional) always "hydra:freetextQuery" # TODO what other values are supported and what do they mean
|
|
16
|
+
# @option variable_map [Boolean] :required (required) is this variable required
|
|
17
|
+
# @option variable_map [String] :default (optional) value to use if a value is not provided in the request (default: '')
|
|
18
|
+
# @option variable_map [Boolean] :encode (optional) whether to url_encode the value (default: false)
|
|
19
|
+
def initialize(variable_map)
|
|
20
|
+
@variable = Qa::LinkedData::Config::Helper.fetch_required(variable_map, :variable, nil)
|
|
21
|
+
@required = Qa::LinkedData::Config::Helper.fetch_boolean(variable_map, :required, nil)
|
|
22
|
+
@default = Qa::LinkedData::Config::Helper.fetch(variable_map, :default, '').to_s
|
|
23
|
+
@encode = Qa::LinkedData::Config::Helper.fetch_boolean(variable_map, :encode, false)
|
|
24
|
+
@property = Qa::LinkedData::Config::Helper.fetch(variable_map, :property, 'hydra:freetextQuery')
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Value to use in substitution, using default if one isn't passed in. Use when template url specifies variable as {var_name}.
|
|
28
|
+
# @param [Object] value to use if it exists
|
|
29
|
+
# @return the value to use (e.g. 'fr')
|
|
30
|
+
def simple_value(sub_value = nil)
|
|
31
|
+
raise Qa::IriTemplate::MissingParameter, "#{variable} is required, but missing" if sub_value.blank? && required?
|
|
32
|
+
return default if sub_value.blank?
|
|
33
|
+
sub_value = sub_value.to_s
|
|
34
|
+
sub_value = ERB::Util.url_encode(sub_value).gsub(".", "%2E") if encode?
|
|
35
|
+
sub_value
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Parameter and value to use in substitution, using default is one isn't passed in. Use when template url specifies variable as {?var_name}.
|
|
39
|
+
# @param [Object] value to use if it exists
|
|
40
|
+
# @return the parameter and value to use (e.g. 'language=fr')
|
|
41
|
+
def parameter_value(sub_value = nil)
|
|
42
|
+
simple_value = simple_value(sub_value)
|
|
43
|
+
return '' if simple_value.blank?
|
|
44
|
+
"#{variable}=#{simple_value}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
# Is this variable required?
|
|
50
|
+
# @return true if required; otherwise, false
|
|
51
|
+
def required?
|
|
52
|
+
@required
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Should the variable's value be encoded?
|
|
56
|
+
# @return true if should encode; otherwise, false
|
|
57
|
+
def encode?
|
|
58
|
+
@encode
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Defines the external authority predicates used to extract additional context from the graph.
|
|
2
|
+
module Qa
|
|
3
|
+
module LinkedData
|
|
4
|
+
module Config
|
|
5
|
+
class ContextMap
|
|
6
|
+
attr_reader :properties # [Array<Qa::LinkedData::Config::ContextPropertyMap>] set of property map models
|
|
7
|
+
|
|
8
|
+
attr_reader :context_map, :groups, :prefixes
|
|
9
|
+
private :context_map, :groups, :prefixes
|
|
10
|
+
|
|
11
|
+
# @param [Hash] context_map that defines groups and properties for additional context to display during the selection process
|
|
12
|
+
# @option context_map [Hash] :groups (optional) predefine group ids and labels to be used in the properties section to group properties
|
|
13
|
+
# @option groups [Hash] key=group_id; value=[Hash] with group_label_i18n and/or group_label_default
|
|
14
|
+
# @option context_map [Array<Hash>] :properties (optional) property maps defining how to get and display the additional context (if none, context will not be added)
|
|
15
|
+
# @param [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#" })
|
|
16
|
+
# @example context_map example
|
|
17
|
+
# {
|
|
18
|
+
# "groups": {
|
|
19
|
+
# "dates": {
|
|
20
|
+
# "group_label_i18n": "qa.linked_data.authority.locnames_ld4l_cache.dates",
|
|
21
|
+
# "group_label_default": "Dates"
|
|
22
|
+
# }
|
|
23
|
+
# },
|
|
24
|
+
# "properties": [
|
|
25
|
+
# {
|
|
26
|
+
# "property_label_i18n": "qa.linked_data.authority.locgenres_ld4l_cache.authoritative_label",
|
|
27
|
+
# "property_label_default": "Authoritative Label",
|
|
28
|
+
# "ldpath": "madsrdf:authoritativeLabel",
|
|
29
|
+
# "selectable": true,
|
|
30
|
+
# "drillable": false
|
|
31
|
+
# },
|
|
32
|
+
# {
|
|
33
|
+
# "group_id": "dates",
|
|
34
|
+
# "property_label_i18n": "qa.linked_data.authority.locnames_ld4l_cache.birth_date",
|
|
35
|
+
# "property_label_default": "Birth",
|
|
36
|
+
# "ldpath": "madsrdf:identifiesRWO/madsrdf:birthDate/schema:label",
|
|
37
|
+
# "selectable": false,
|
|
38
|
+
# "drillable": false
|
|
39
|
+
# }
|
|
40
|
+
# ]
|
|
41
|
+
# }
|
|
42
|
+
def initialize(context_map = {}, prefixes = {})
|
|
43
|
+
@context_map = context_map
|
|
44
|
+
@prefixes = prefixes
|
|
45
|
+
extract_groups
|
|
46
|
+
extract_properties
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def group_label(group_id)
|
|
50
|
+
groups[group_id]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def extract_properties
|
|
56
|
+
@properties = []
|
|
57
|
+
properties_map = Qa::LinkedData::Config::Helper.fetch(context_map, :properties, {})
|
|
58
|
+
properties_map.each { |property_map| @properties << ContextPropertyMap.new(property_map, prefixes) }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def extract_groups
|
|
62
|
+
@groups = {}
|
|
63
|
+
groups_map = Qa::LinkedData::Config::Helper.fetch(context_map, :groups, {})
|
|
64
|
+
groups_map.each { |group_id, group_map| add_group(group_id, group_map) }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def add_group(group_id, group_map)
|
|
68
|
+
return if groups.key? group_id
|
|
69
|
+
i18n_key = Qa::LinkedData::Config::Helper.fetch(group_map, :group_label_i18n, nil)
|
|
70
|
+
default = Qa::LinkedData::Config::Helper.fetch(group_map, :group_label_default, nil)
|
|
71
|
+
return groups[group_id] = I18n.t(i18n_key, default: default) if i18n_key.present?
|
|
72
|
+
groups[group_id] = default
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,144 @@
|
|
|
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
|
+
module Config
|
|
7
|
+
class ContextPropertyMap
|
|
8
|
+
VALUE_ON_ERROR = [].freeze
|
|
9
|
+
|
|
10
|
+
attr_reader :group_id, # id that identifies which group the property should be in
|
|
11
|
+
:label
|
|
12
|
+
|
|
13
|
+
attr_reader :property_map, :ldpath, :expansion_label_ldpath, :expansion_id_ldpath, :prefixes
|
|
14
|
+
private :property_map, :ldpath, :expansion_label_ldpath, :expansion_id_ldpath, :prefixes
|
|
15
|
+
|
|
16
|
+
# @param [Hash] property_map defining information to return to provide context
|
|
17
|
+
# @option property_map [String] :group_id (optional) default label to use for a property (default: no label)
|
|
18
|
+
# @option property_map [String] :property_label_i18n (optional) i18n key to use to get the label for a property (default: property_label_default OR no label if neither are defined)
|
|
19
|
+
# @option property_map [String] :property_label_default (optional) default label to use for a property (default: no label)
|
|
20
|
+
# @option property_map [String] :ldpath (required) identifies the values to extract from the graph (based on http://marmotta.apache.org/ldpath/language.html)
|
|
21
|
+
# @option property_map [Boolean] :selectable (optional) if true, this property can selected as the value (default: false)
|
|
22
|
+
# @option property_map [Boolean] :drillable (optional) if true, the label for this property can be used to execute a second query allowing navi (default: false)
|
|
23
|
+
# @param [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#" })
|
|
24
|
+
# @example property_map example
|
|
25
|
+
# {
|
|
26
|
+
# "group_id": "dates",
|
|
27
|
+
# "property_label_i18n": "qa.linked_data.authority.locnames_ld4l_cache.birth_date",
|
|
28
|
+
# "property_label_default": "Birth",
|
|
29
|
+
# "ldpath": "madsrdf:identifiesRWO/madsrdf:birthDate/schema:label",
|
|
30
|
+
# "selectable": false,
|
|
31
|
+
# "drillable": false
|
|
32
|
+
# }
|
|
33
|
+
def initialize(property_map = {}, prefixes = {})
|
|
34
|
+
@property_map = property_map
|
|
35
|
+
@group_id = Qa::LinkedData::Config::Helper.fetch_symbol(property_map, :group_id, nil)
|
|
36
|
+
@label = extract_label
|
|
37
|
+
@ldpath = Qa::LinkedData::Config::Helper.fetch_required(property_map, :ldpath, false)
|
|
38
|
+
@selectable = Qa::LinkedData::Config::Helper.fetch_boolean(property_map, :selectable, false)
|
|
39
|
+
@drillable = Qa::LinkedData::Config::Helper.fetch_boolean(property_map, :drillable, false)
|
|
40
|
+
@expansion_label_ldpath = Qa::LinkedData::Config::Helper.fetch(property_map, :expansion_label_ldpath, nil)
|
|
41
|
+
@expansion_id_ldpath = Qa::LinkedData::Config::Helper.fetch(property_map, :expansion_id_ldpath, nil)
|
|
42
|
+
@prefixes = prefixes
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Can this property be the selected value?
|
|
46
|
+
# @return [Boolean] true if this property's value can be selected; otherwise, false
|
|
47
|
+
def selectable?
|
|
48
|
+
@selectable
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Can this property be used as a new query
|
|
52
|
+
# @return [Boolean] true if this property's value can be used to drill up/down to another level; otherwise, false
|
|
53
|
+
def drillable?
|
|
54
|
+
@drillable
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def group?
|
|
58
|
+
group_id.present?
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Should this URI value be expanded to include its label?
|
|
62
|
+
# @return [Boolean] true if this property's value is expected to be a URI and its label should be included in the value; otherwise, false
|
|
63
|
+
def expand_uri?
|
|
64
|
+
expansion_label_ldpath.present?
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Values of this property for a specfic subject URI
|
|
68
|
+
# @return [Array<String>] values for this property
|
|
69
|
+
def values(graph, subject_uri)
|
|
70
|
+
ldpath_evaluate(basic_program, graph, subject_uri)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Values of this property for a specfic subject URI with URI values expanded to include id and label.
|
|
74
|
+
# @return [Array<Hash>] expanded values for this property
|
|
75
|
+
# @example returned values
|
|
76
|
+
# [{
|
|
77
|
+
# uri: "http://id.loc.gov/authorities/genreForms/gf2014026551",
|
|
78
|
+
# id: "gf2014026551",
|
|
79
|
+
# label: "Space operas"
|
|
80
|
+
# }]
|
|
81
|
+
def expanded_values(graph, subject_uri)
|
|
82
|
+
values = values(graph, subject_uri)
|
|
83
|
+
return values unless expand_uri?
|
|
84
|
+
return values unless values.respond_to? :map!
|
|
85
|
+
values.map! do |uri|
|
|
86
|
+
{ uri: uri, id: expansion_id(graph, uri), label: expansion_label(graph, uri) }
|
|
87
|
+
end
|
|
88
|
+
values
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
|
|
93
|
+
def extract_label
|
|
94
|
+
i18n_key = Qa::LinkedData::Config::Helper.fetch(property_map, :property_label_i18n, nil)
|
|
95
|
+
default = Qa::LinkedData::Config::Helper.fetch(property_map, :property_label_default, nil)
|
|
96
|
+
return I18n.t(i18n_key, default: default) if i18n_key.present?
|
|
97
|
+
default
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def basic_program
|
|
101
|
+
@basic_program ||= ldpath_program(ldpath)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def expansion_label_program
|
|
105
|
+
@expansion_label_program ||= ldpath_program(expansion_label_ldpath)
|
|
106
|
+
end
|
|
107
|
+
|
|
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')
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def expansion_label(graph, uri)
|
|
132
|
+
label = ldpath_evaluate(expansion_label_program, graph, RDF::URI(uri))
|
|
133
|
+
label.size == 1 ? label.first : label
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def expansion_id(graph, uri)
|
|
137
|
+
return uri if expansion_id_ldpath.blank?
|
|
138
|
+
id = ldpath_evaluate(expansion_id_program, graph, RDF::URI(uri))
|
|
139
|
+
id.size == 1 ? id.first : id
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|