qa 0.4.3 → 0.5.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 +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
|