qa 0.4.3 → 0.5.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 +29 -13
- data/app/controllers/qa/terms_controller.rb +7 -3
- data/config/routes.rb +3 -3
- data/lib/qa.rb +2 -2
- data/lib/qa/authorities/authority_with_sub_authority.rb +17 -8
- data/lib/qa/authorities/base.rb +1 -1
- data/lib/qa/authorities/getty.rb +6 -52
- data/lib/qa/authorities/getty/aat.rb +55 -0
- data/lib/qa/authorities/loc.rb +9 -64
- data/lib/qa/authorities/loc/generic_authority.rb +71 -0
- data/lib/qa/authorities/local.rb +66 -31
- data/lib/qa/authorities/local/file_based_authority.rb +49 -0
- data/lib/qa/authorities/local_subauthority.rb +5 -5
- data/lib/qa/authorities/oclcts.rb +11 -32
- data/lib/qa/authorities/oclcts/generic_oclc_authority.rb +42 -0
- data/lib/qa/version.rb +1 -1
- data/spec/controllers/terms_controller_spec.rb +9 -9
- data/spec/internal/db/development.sqlite3 +0 -0
- data/spec/internal/log/development.log +10910 -0
- data/spec/lib/{authorities_local_spec.rb → authorities/file_based_authority_spec.rb} +8 -20
- data/spec/lib/{authorities_getty_spec.rb → authorities/getty/aat_spec.rb} +13 -19
- data/spec/lib/authorities/getty_spec.rb +30 -0
- data/spec/lib/{authorities_loc_spec.rb → authorities/loc_spec.rb} +12 -11
- data/spec/lib/authorities/local_spec.rb +48 -0
- data/spec/lib/{authorities_mesh_spec.rb → authorities/mesh_spec.rb} +0 -0
- data/spec/lib/{authorities_oclcts_spec.rb → authorities/oclcts_spec.rb} +2 -2
- data/spec/lib/{authorities_tgnlang_spec.rb → authorities/tgnlang_spec.rb} +0 -0
- data/spec/lib/authorities_loc_subauthorities.rb +3 -3
- data/spec/lib/authorities_local_subauthorities_spec.rb +3 -3
- data/spec/routing/route_spec.rb +3 -3
- metadata +22 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6edd488c2f33caee0d368664c08a2f4ea70f3b9e
|
4
|
+
data.tar.gz: 0bc9d51827a5ece5ce2bf71d8b184d232d859c6b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 592b389388e0fde9fd85996694ecb44a5bd477cbf78c4b10d066f757989a0d4a6d842d3832f8475bb4b77040afec66131db8ed8df52a4ae81de3789d7c7fffb1
|
7
|
+
data.tar.gz: 406889f087a33c4089b0f72e43e91f4a542e3569c29116f3657155f30a8591e6a8140c8785ec1fc70e9ac061b9dcb43c371a8b526ee15b5543dbe9fbcbe7b4e8
|
data/README.md
CHANGED
@@ -51,17 +51,17 @@ Start questioning your authorities!
|
|
51
51
|
Return a complete list of terms:
|
52
52
|
|
53
53
|
/qa/terms/:vocab
|
54
|
-
/qa/terms/:vocab/:
|
54
|
+
/qa/terms/:vocab/:subauthority
|
55
55
|
|
56
56
|
Return a set of terms matching a given query
|
57
57
|
|
58
58
|
/qa/search/:vocab?q=search_term
|
59
|
-
/qa/search/:vocab/:
|
59
|
+
/qa/search/:vocab/:subauthority?q=search_term
|
60
60
|
|
61
61
|
Return the complete information for a specific term given its identifier
|
62
62
|
|
63
63
|
/qa/show/:vocab/:id
|
64
|
-
/qa/show/:vocab/:
|
64
|
+
/qa/show/:vocab/:subauthority/:id
|
65
65
|
|
66
66
|
### JSON Results
|
67
67
|
|
@@ -136,7 +136,7 @@ authority YAML files are located in `config/authorities/`. This location can be
|
|
136
136
|
the `:local_path` entry in `config/authorities.yml`. Relative paths are assumed to be relative to
|
137
137
|
`Rails.root`.
|
138
138
|
|
139
|
-
Local authority YAML files are named for the sub-authority they represent. The included example "states" sub-authority
|
139
|
+
Local authority YAML files are named for the sub-authority they represent. The included example "states" sub-authority
|
140
140
|
is named states.yml.
|
141
141
|
|
142
142
|
To create your own local authority, create a .yml file, place it in the configured directory and query it
|
@@ -148,19 +148,35 @@ using the file's name as the sub-authority. For example, if I create `foo.yml`,
|
|
148
148
|
|
149
149
|
##### List of terms
|
150
150
|
|
151
|
-
|
151
|
+
terms:
|
152
152
|
- Term 1
|
153
153
|
- Term 2
|
154
|
-
|
154
|
+
|
155
155
|
##### List of id and term keys and, optionally, active key
|
156
156
|
|
157
|
-
|
158
|
-
-
|
159
|
-
|
160
|
-
|
161
|
-
-
|
162
|
-
|
163
|
-
|
157
|
+
terms:
|
158
|
+
- id: id1
|
159
|
+
term: Term 1
|
160
|
+
active: true
|
161
|
+
- id: id2
|
162
|
+
term: Term 2
|
163
|
+
active: false
|
164
|
+
|
165
|
+
|
166
|
+
#### Adding your own local authorities
|
167
|
+
|
168
|
+
If you'd like to add your own local authority that isn't necessarily backed by yaml, create an initializer and tell the local authority about your custom sub-authority:
|
169
|
+
|
170
|
+
```ruby
|
171
|
+
Qa::Authorities::Local.register_subauthority('names', 'LocalNames')
|
172
|
+
```
|
173
|
+
|
174
|
+
The second argument is a name of a class that represents your local authority. Then when you go to:
|
175
|
+
|
176
|
+
/qa/search/local/names?q=Zoia
|
177
|
+
|
178
|
+
You'll be searching with an instance of `LocalNames`
|
179
|
+
|
164
180
|
|
165
181
|
### Medical Subject Headings (MeSH)
|
166
182
|
|
@@ -30,15 +30,19 @@ class Qa::TermsController < ApplicationController
|
|
30
30
|
|
31
31
|
def init_authority
|
32
32
|
begin
|
33
|
-
|
33
|
+
mod = authority_class.constantize
|
34
34
|
rescue NameError => e
|
35
35
|
logger.warn "Unable to initialize authority #{authority_class}"
|
36
36
|
head :not_found
|
37
37
|
return
|
38
38
|
end
|
39
|
-
args = [params[:sub_authority]].compact
|
40
39
|
begin
|
41
|
-
@authority =
|
40
|
+
@authority = if mod.kind_of? Class
|
41
|
+
mod.new
|
42
|
+
else
|
43
|
+
raise Qa::MissingSubAuthority, "No sub-authority provided" if params[:subauthority].blank?
|
44
|
+
mod.subauthority_for(params[:subauthority])
|
45
|
+
end
|
42
46
|
rescue Qa::InvalidSubAuthority, Qa::MissingSubAuthority => e
|
43
47
|
logger.warn e.message
|
44
48
|
head :not_found
|
data/config/routes.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Qa::Engine.routes.draw do
|
2
|
-
get "/terms/:vocab(/:
|
3
|
-
get "/search/:vocab(/:
|
2
|
+
get "/terms/:vocab(/:subauthority)", controller: :terms, action: :index
|
3
|
+
get "/search/:vocab(/:subauthority)", controller: :terms, action: :search
|
4
4
|
get "/show/:vocab/:id", controller: :terms, action: :show
|
5
|
-
get "/show/:vocab/:
|
5
|
+
get "/show/:vocab/:subauthority/:id", controller: :terms, action: :show
|
6
6
|
end
|
data/lib/qa.rb
CHANGED
@@ -10,9 +10,9 @@ module Qa
|
|
10
10
|
# Raised when the configuration directory for local authorities doesn't exist
|
11
11
|
class ConfigDirectoryNotFound < StandardError; end
|
12
12
|
|
13
|
-
# Raised when a
|
13
|
+
# Raised when a subauthority is not passed into an authority
|
14
14
|
class MissingSubAuthority < ArgumentError; end
|
15
15
|
|
16
|
-
# Raised when a
|
16
|
+
# Raised when a subauthority is not valid
|
17
17
|
class InvalidSubAuthority < ArgumentError; end
|
18
18
|
end
|
@@ -1,17 +1,26 @@
|
|
1
1
|
module Qa::Authorities
|
2
|
-
|
3
|
-
attr_reader :sub_authority
|
2
|
+
module AuthorityWithSubAuthority
|
4
3
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
def new(subauthority=nil)
|
5
|
+
raise "Initializing with as sub authority is removed. use #{self.class}.subauthority_for(#{subauthority.inspect}) instead"
|
6
|
+
end
|
7
|
+
|
8
|
+
def subauthority_for(subauthority)
|
9
|
+
validate_subauthority!(subauthority)
|
10
|
+
subauthority_class(subauthority).new
|
11
|
+
end
|
12
|
+
|
13
|
+
def subauthority_class(name)
|
14
|
+
[self, name].join('::').classify.constantize
|
15
|
+
end
|
16
|
+
|
17
|
+
def validate_subauthority!(subauthority)
|
18
|
+
raise Qa::InvalidSubAuthority, "Unable to initialize sub-authority #{subauthority} for #{self}. Valid sub-authorities are #{subauthorities.inspect}" unless subauthorities.include?(subauthority)
|
10
19
|
end
|
11
20
|
|
12
21
|
# By default, an authority has no subauthorities unless they
|
13
22
|
# are defined by the subclassed authority.
|
14
|
-
def
|
23
|
+
def subauthorities
|
15
24
|
[]
|
16
25
|
end
|
17
26
|
end
|
data/lib/qa/authorities/base.rb
CHANGED
data/lib/qa/authorities/getty.rb
CHANGED
@@ -1,63 +1,17 @@
|
|
1
1
|
require 'uri'
|
2
2
|
|
3
3
|
module Qa::Authorities
|
4
|
-
|
5
|
-
|
4
|
+
module Getty
|
5
|
+
require 'qa/authorities/getty/aat'
|
6
|
+
extend AuthorityWithSubAuthority
|
6
7
|
|
7
|
-
def
|
8
|
+
def self.subauthorities
|
8
9
|
[ "aat" ]
|
9
10
|
end
|
10
11
|
|
11
|
-
def
|
12
|
-
|
12
|
+
def self.subauthority_class(_)
|
13
|
+
AAT
|
13
14
|
end
|
14
|
-
|
15
|
-
# get_json is not ideomatic, so we'll make an alias
|
16
|
-
def json(*args)
|
17
|
-
get_json(*args)
|
18
|
-
end
|
19
|
-
|
20
|
-
def build_query_url q
|
21
|
-
query = URI.escape(sparql(untaint(q)))
|
22
|
-
"http://vocab.getty.edu/sparql.json?query=#{URI.escape(sparql(q))}&_implicit=false&implicit=true&_equivalent=false&_form=%2Fsparql"
|
23
|
-
end
|
24
|
-
|
25
|
-
def sparql(q)
|
26
|
-
search = untaint(q)
|
27
|
-
# The full text index matches on fields besides the term, so we filter to ensure the match is in the term.
|
28
|
-
sparql = "SELECT ?s ?name {
|
29
|
-
?s a skos:Concept; luc:term \"#{search}\";
|
30
|
-
skos:inScheme <http://vocab.getty.edu/#{@sub_authority}/> ;
|
31
|
-
gvp:prefLabelGVP [skosxl:literalForm ?name].
|
32
|
-
FILTER regex(?name, \"#{search}\", \"i\") .
|
33
|
-
} LIMIT 10"
|
34
|
-
end
|
35
|
-
|
36
|
-
def untaint(q)
|
37
|
-
q.gsub(/[^\w\s-]/, '')
|
38
|
-
end
|
39
|
-
|
40
|
-
def find id
|
41
|
-
json(find_url(id))
|
42
|
-
end
|
43
|
-
|
44
|
-
def find_url id
|
45
|
-
"http://vocab.getty.edu/#{@sub_authority}/#{id}.json"
|
46
|
-
end
|
47
|
-
|
48
|
-
def request_options
|
49
|
-
{ accept: 'application/sparql-results+json'}
|
50
|
-
end
|
51
|
-
|
52
|
-
private
|
53
|
-
|
54
|
-
# Reformats the data received from the LOC service
|
55
|
-
def parse_authority_response(response)
|
56
|
-
response['results']['bindings'].map do |result|
|
57
|
-
{ 'id' => result['s']['value'], 'label' => result['name']['value'] }
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
15
|
end
|
62
16
|
end
|
63
17
|
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Qa::Authorities
|
2
|
+
class Getty::AAT < Base
|
3
|
+
include WebServiceBase
|
4
|
+
def search q
|
5
|
+
parse_authority_response(json(build_query_url(q)))
|
6
|
+
end
|
7
|
+
|
8
|
+
# get_json is not ideomatic, so we'll make an alias
|
9
|
+
def json(*args)
|
10
|
+
get_json(*args)
|
11
|
+
end
|
12
|
+
|
13
|
+
def build_query_url q
|
14
|
+
query = URI.escape(sparql(untaint(q)))
|
15
|
+
"http://vocab.getty.edu/sparql.json?query=#{URI.escape(sparql(q))}&_implicit=false&implicit=true&_equivalent=false&_form=%2Fsparql"
|
16
|
+
end
|
17
|
+
|
18
|
+
def sparql(q)
|
19
|
+
search = untaint(q)
|
20
|
+
# The full text index matches on fields besides the term, so we filter to ensure the match is in the term.
|
21
|
+
sparql = "SELECT ?s ?name {
|
22
|
+
?s a skos:Concept; luc:term \"#{search}\";
|
23
|
+
skos:inScheme <http://vocab.getty.edu/aat/> ;
|
24
|
+
gvp:prefLabelGVP [skosxl:literalForm ?name].
|
25
|
+
FILTER regex(?name, \"#{search}\", \"i\") .
|
26
|
+
} LIMIT 10"
|
27
|
+
end
|
28
|
+
|
29
|
+
def untaint(q)
|
30
|
+
q.gsub(/[^\w\s-]/, '')
|
31
|
+
end
|
32
|
+
|
33
|
+
def find id
|
34
|
+
json(find_url(id))
|
35
|
+
end
|
36
|
+
|
37
|
+
def find_url id
|
38
|
+
"http://vocab.getty.edu/aat/#{id}.json"
|
39
|
+
end
|
40
|
+
|
41
|
+
def request_options
|
42
|
+
{ accept: 'application/sparql-results+json'}
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# Reformats the data received from the LOC service
|
48
|
+
def parse_authority_response(response)
|
49
|
+
response['results']['bindings'].map do |result|
|
50
|
+
{ 'id' => result['s']['value'], 'label' => result['name']['value'] }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
data/lib/qa/authorities/loc.rb
CHANGED
@@ -1,73 +1,18 @@
|
|
1
1
|
require 'uri'
|
2
2
|
|
3
3
|
module Qa::Authorities
|
4
|
-
|
5
|
-
|
6
|
-
include LocSubauthority
|
4
|
+
module Loc
|
5
|
+
extend AuthorityWithSubAuthority
|
7
6
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
def search q
|
13
|
-
@raw_response = get_json(build_query_url(q))
|
14
|
-
parse_authority_response
|
15
|
-
end
|
16
|
-
|
17
|
-
def build_query_url q
|
18
|
-
escaped_query = URI.escape(q)
|
19
|
-
authority_fragment = get_url_for_authority(sub_authority) + URI.escape(sub_authority)
|
20
|
-
return "http://id.loc.gov/search/?q=#{escaped_query}&q=#{authority_fragment}&format=json"
|
21
|
-
end
|
22
|
-
|
23
|
-
def find id
|
24
|
-
get_json(find_url(id))
|
25
|
-
end
|
26
|
-
|
27
|
-
def find_url id
|
28
|
-
"http://id.loc.gov/authorities/#{@sub_authority}/#{id}.json"
|
7
|
+
require 'qa/authorities/loc/generic_authority'
|
8
|
+
def self.subauthority_for(subauthority)
|
9
|
+
validate_subauthority!(subauthority)
|
10
|
+
GenericAuthority.new(subauthority)
|
29
11
|
end
|
30
12
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
def parse_authority_response
|
35
|
-
@raw_response.select {|response| response[0] == "atom:entry"}.map do |response|
|
36
|
-
loc_response_to_qa(response_to_struct(response))
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
# Converts most of the atom data into an OpenStruct object.
|
41
|
-
#
|
42
|
-
# Note that this is a pretty naive conversion. There should probably just
|
43
|
-
# be a class that properly translates and stores the various pieces of
|
44
|
-
# data, especially if this logic could be useful in other auth lookups.
|
45
|
-
def response_to_struct response
|
46
|
-
result = response.each_with_object({}) do |result_parts, result|
|
47
|
-
next unless result_parts[0]
|
48
|
-
key = result_parts[0].sub('atom:', '').sub('dcterms:', '')
|
49
|
-
info = result_parts[1]
|
50
|
-
val = result_parts[2]
|
51
|
-
|
52
|
-
case key
|
53
|
-
when 'title', 'id', 'name', 'updated', 'created'
|
54
|
-
result[key] = val
|
55
|
-
when 'link'
|
56
|
-
result["links"] ||= []
|
57
|
-
result["links"] << [info["type"], info["href"]]
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
OpenStruct.new(result)
|
62
|
-
end
|
63
|
-
|
64
|
-
# Simple conversion from LoC-based struct to QA hash
|
65
|
-
def loc_response_to_qa data
|
66
|
-
{
|
67
|
-
"id" => data.id || data.title,
|
68
|
-
"label" => data.title
|
69
|
-
}
|
13
|
+
extend LocSubauthority
|
14
|
+
def self.subauthorities
|
15
|
+
authorities + vocabularies + datatypes + preservation
|
70
16
|
end
|
71
|
-
|
72
17
|
end
|
73
18
|
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Qa::Authorities
|
2
|
+
class Loc::GenericAuthority < Base
|
3
|
+
attr_reader :subauthority
|
4
|
+
def initialize(subauthority)
|
5
|
+
@subauthority = subauthority
|
6
|
+
end
|
7
|
+
|
8
|
+
include WebServiceBase
|
9
|
+
|
10
|
+
def search q
|
11
|
+
@raw_response = get_json(build_query_url(q))
|
12
|
+
parse_authority_response
|
13
|
+
end
|
14
|
+
|
15
|
+
def build_query_url q
|
16
|
+
escaped_query = URI.escape(q)
|
17
|
+
authority_fragment = Loc.get_url_for_authority(subauthority) + URI.escape(subauthority)
|
18
|
+
return "http://id.loc.gov/search/?q=#{escaped_query}&q=#{authority_fragment}&format=json"
|
19
|
+
end
|
20
|
+
|
21
|
+
def find id
|
22
|
+
get_json(find_url(id))
|
23
|
+
end
|
24
|
+
|
25
|
+
def find_url id
|
26
|
+
"http://id.loc.gov/authorities/#{@subauthority}/#{id}.json"
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# Reformats the data received from the LOC service
|
32
|
+
def parse_authority_response
|
33
|
+
@raw_response.select {|response| response[0] == "atom:entry"}.map do |response|
|
34
|
+
loc_response_to_qa(response_to_struct(response))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Converts most of the atom data into an OpenStruct object.
|
39
|
+
#
|
40
|
+
# Note that this is a pretty naive conversion. There should probably just
|
41
|
+
# be a class that properly translates and stores the various pieces of
|
42
|
+
# data, especially if this logic could be useful in other auth lookups.
|
43
|
+
def response_to_struct response
|
44
|
+
result = response.each_with_object({}) do |result_parts, result|
|
45
|
+
next unless result_parts[0]
|
46
|
+
key = result_parts[0].sub('atom:', '').sub('dcterms:', '')
|
47
|
+
info = result_parts[1]
|
48
|
+
val = result_parts[2]
|
49
|
+
|
50
|
+
case key
|
51
|
+
when 'title', 'id', 'name', 'updated', 'created'
|
52
|
+
result[key] = val
|
53
|
+
when 'link'
|
54
|
+
result["links"] ||= []
|
55
|
+
result["links"] << [info["type"], info["href"]]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
OpenStruct.new(result)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Simple conversion from LoC-based struct to QA hash
|
63
|
+
def loc_response_to_qa data
|
64
|
+
{
|
65
|
+
"id" => data.id || data.title,
|
66
|
+
"label" => data.title
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|