triannon 0.4.4 → 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 +25 -5
- data/app/models/triannon/annotation.rb +24 -113
- data/app/services/triannon/ldp_writer.rb +1 -1
- data/app/services/triannon/solr_writer.rb +53 -0
- data/app/views/triannon/annotations/index.html.erb +1 -1
- data/app/views/triannon/annotations/show.html.erb +1 -1
- data/config/solr/solr.xml +36 -0
- data/config/solr/{schema.xml → triannon-core/conf/schema.xml} +0 -0
- data/config/solr/{solrconfig.xml → triannon-core/conf/solrconfig.xml} +1 -1
- data/config/triannon.yml +11 -2
- data/lib/generators/triannon/install_generator.rb +8 -2
- data/lib/triannon.rb +3 -1
- data/lib/triannon/graph.rb +131 -3
- data/lib/triannon/version.rb +1 -1
- metadata +34 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9061e8fc2e0127540ae750118289a5f79a7f6969
|
4
|
+
data.tar.gz: 2785857023cdd9a5a2e5caa3ecb7810f613430cb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e149fc61982e7323f0a2401a316737f0dc0132fc2f3374f167317b149c0ec9541c47e5ec5364596c977c3d55f96bb006b213d5c78e966d091b770098d18fd5d4
|
7
|
+
data.tar.gz: edac1a83ba367c2064a0ba1c17662948531634873924c0740a947e272bf90004a8d2128edc9b2e73589fae1522b3d24dcedccec458459a7ee3d062de860d974d
|
data/README.md
CHANGED
@@ -35,6 +35,7 @@ $ rails g triannon:install
|
|
35
35
|
Edit the `config/triannon.yml` file:
|
36
36
|
|
37
37
|
* `ldp_url:` Points to the root annotations container on your LDP server
|
38
|
+
* `solr_url:` Points to the baseurl of Solr instance configured for Triannon
|
38
39
|
* `triannon_base_url:` Used as the base url for all annotations hosted by your Triannon server. Identifiers from the LDP server will be appended to this base-url. Generally something like "https://your-triannon-rails-box/annotations", as "/annotations" is added to the path by the Triannon gem
|
39
40
|
|
40
41
|
Generate the root annotations container on the LDP server
|
@@ -45,20 +46,39 @@ $ rake triannon:create_root_container
|
|
45
46
|
|
46
47
|
## Running the application in development
|
47
48
|
|
48
|
-
There is a bundled rake task for running the test app
|
49
|
+
There is a bundled rake task for running the test app, but there is some one-time set up.
|
49
50
|
|
51
|
+
### One time setup
|
52
|
+
|
53
|
+
##### Set up a local instance of Fedora4
|
50
54
|
```console
|
51
|
-
# One time setup: run the following 3 commands
|
52
55
|
$ rake jetty:download
|
53
56
|
$ rake jetty:unzip
|
57
|
+
```
|
58
|
+
##### Set up a Triannon flavored Solr
|
59
|
+
```console
|
60
|
+
$ cp config/solr/solr.xml jetty/solr
|
61
|
+
$ cp config/solr/triannon-core jetty/solr
|
62
|
+
```
|
63
|
+
|
64
|
+
##### Set up a runnable Rails app that uses triannon gem
|
65
|
+
```console
|
54
66
|
$ rake engine_cart:generate # (first run only)
|
67
|
+
```
|
55
68
|
|
56
|
-
|
57
|
-
|
69
|
+
##### Configure spec/internal/config/triannon.yml as specified above
|
70
|
+
```console
|
71
|
+
$ vi spec/internal/config/triannon.yml
|
72
|
+
```
|
58
73
|
|
59
|
-
|
74
|
+
##### Generate root annotations container
|
75
|
+
```console
|
60
76
|
$ rake triannon:create_root_container
|
77
|
+
```
|
61
78
|
|
62
79
|
# Run the test app
|
80
|
+
```console
|
81
|
+
$ rake jetty:start
|
63
82
|
$ rake triannon:server
|
83
|
+
$ rake jetty:stop # at some point
|
64
84
|
```
|
@@ -59,143 +59,50 @@ module Triannon
|
|
59
59
|
end
|
60
60
|
|
61
61
|
def graph
|
62
|
-
@graph ||=
|
62
|
+
@graph ||= begin
|
63
|
+
g = data_to_graph
|
64
|
+
Triannon::Graph.new g if g.kind_of? RDF::Graph
|
65
|
+
end
|
63
66
|
end
|
64
67
|
|
68
|
+
# @param [RDF::Graph]
|
65
69
|
def graph= g
|
66
|
-
@graph = g
|
70
|
+
@graph = Triannon::Graph.new g if g.kind_of? RDF::Graph
|
67
71
|
end
|
68
72
|
|
69
|
-
# @return json-ld representation of
|
73
|
+
# @return json-ld representation of anno with OpenAnnotation context as a url
|
70
74
|
def jsonld_oa
|
71
|
-
|
72
|
-
hash_from_json = JSON.parse(inline_context)
|
73
|
-
hash_from_json["@context"] = Triannon::JsonldContext::OA_CONTEXT_URL
|
74
|
-
hash_from_json.to_json
|
75
|
+
graph.jsonld_oa
|
75
76
|
end
|
76
77
|
|
77
|
-
# @return json-ld representation of
|
78
|
+
# @return json-ld representation of anno with IIIF context as a url
|
78
79
|
def jsonld_iiif
|
79
|
-
|
80
|
-
hash_from_json = JSON.parse(inline_context)
|
81
|
-
hash_from_json["@context"] = Triannon::JsonldContext::IIIF_CONTEXT_URL
|
82
|
-
hash_from_json.to_json
|
83
|
-
end
|
84
|
-
|
85
|
-
# @return [String] the id of this annotation as a url
|
86
|
-
# TODO: re-usable as part of Triannon::Graph class?
|
87
|
-
def url
|
88
|
-
if graph_exists?
|
89
|
-
solution = graph.query Triannon::Graph.anno_query
|
90
|
-
if solution && solution.size == 1
|
91
|
-
solution.first.s.to_s
|
92
|
-
# TODO: raise exception if no URL?
|
93
|
-
end
|
94
|
-
end
|
80
|
+
graph.jsonld_iiif
|
95
81
|
end
|
96
82
|
|
97
|
-
#
|
98
|
-
|
99
|
-
|
100
|
-
if graph_exists?
|
101
|
-
q = RDF::Query.new
|
102
|
-
q << [:s, RDF::OpenAnnotation.hasTarget, nil] # must have a target
|
103
|
-
q << [:s, RDF.type, :type]
|
104
|
-
solution = graph.query q
|
105
|
-
solution.distinct!
|
106
|
-
if solution && solution.size == 1
|
107
|
-
solution.first.type.to_s
|
108
|
-
# TODO: raise exception if no type?
|
109
|
-
end
|
110
|
-
end
|
83
|
+
# @return [String] the id of this annotation as a url or nil if no graph
|
84
|
+
def id_as_url
|
85
|
+
graph.id_as_url if graph_exists?
|
111
86
|
end
|
112
87
|
|
113
|
-
# @return [Array] of
|
114
|
-
# TODO: re-usable as part of Triannon::Graph class?
|
88
|
+
# @return [Array<String>] of urls expressing the OA motivated_by values or nil if no graph
|
115
89
|
def motivated_by
|
116
|
-
if graph_exists?
|
117
|
-
q = Triannon::Graph.anno_query.dup
|
118
|
-
q << [:s, RDF::OpenAnnotation.motivatedBy, :motivated_by]
|
119
|
-
solution = graph.query q
|
120
|
-
if solution && solution.size > 0
|
121
|
-
motivations = []
|
122
|
-
solution.each {|res|
|
123
|
-
motivations << res.motivated_by.to_s
|
124
|
-
}
|
125
|
-
motivations
|
126
|
-
# TODO: raise exception if none?
|
127
|
-
end
|
128
|
-
end
|
90
|
+
graph.motivated_by if graph_exists?
|
129
91
|
end
|
130
92
|
|
131
93
|
protected
|
132
94
|
|
133
|
-
#
|
95
|
+
# Add annotation to Solr as a Solr document
|
134
96
|
def solr_save
|
135
|
-
#
|
136
|
-
|
97
|
+
# pass in id we got from LDP Store
|
98
|
+
solr_writer.add(graph.solr_hash(id))
|
137
99
|
end
|
138
100
|
|
139
|
-
#
|
101
|
+
# Delete annotation from Solr
|
140
102
|
def solr_delete
|
141
|
-
|
103
|
+
solr_writer.delete(id)
|
142
104
|
end
|
143
105
|
|
144
|
-
# TODO: WRITE_COMMENTS_AND_TESTS_FOR_THIS_METHOD
|
145
|
-
# TODO: re-usable as part of Triannon::Graph class?
|
146
|
-
#
|
147
|
-
# @return [Hash] a hash to be written to Solr, populated appropriately
|
148
|
-
def solr_hash
|
149
|
-
doc_hash = {}
|
150
|
-
tid = url.sub(Triannon.config[:ldp_url], "")
|
151
|
-
tid.sub(/^\//, "")
|
152
|
-
doc_hash[:id] = tid
|
153
|
-
doc_hash[:motivation] = motivated_by.map { |m| m.sub(RDF::OpenAnnotation.to_s, "") }
|
154
|
-
# date field format: 1995-12-31T23:59:59Z; or w fractional seconds: 1995-12-31T23:59:59.999Z
|
155
|
-
# doc_hash[:annotated_at] =
|
156
|
-
# doc_hash[:annotated_by_stem]
|
157
|
-
doc_hash[:target_url] = predicate_urls RDF::OpenAnnotation.hasTarget
|
158
|
-
doc_hash[:target_type] = ['external_URI'] if doc_hash[:target_url].size > 0
|
159
|
-
doc_hash[:body_url] = predicate_urls RDF::OpenAnnotation.hasBody
|
160
|
-
doc_hash[:body_type] = []
|
161
|
-
doc_hash[:body_type] << 'external_URI' if doc_hash[:body_url].size > 0
|
162
|
-
doc_hash[:body_chars_exact] = body_chars
|
163
|
-
doc_hash[:body_type] << 'content_as_text' if doc_hash[:body_chars_exact].size > 0
|
164
|
-
doc_hash[:body_type] << 'no_body' if doc_hash[:body_type].size == 0
|
165
|
-
doc_hash[:anno_jsonld] = jsonld_oa
|
166
|
-
doc_hash
|
167
|
-
end
|
168
|
-
|
169
|
-
# TODO: WRITE_COMMENTS_AND_TESTS_FOR_THIS_METHOD
|
170
|
-
# TODO: re-usable as part of Triannon::Graph class?
|
171
|
-
# @param [RDF::URI] predicate either RDF::OpenAnnotation.hasTarget or RDF::OpenAnnotation.hasBody
|
172
|
-
# @return [Array<String>] urls for the predicate, as an Array of Strings
|
173
|
-
def predicate_urls(predicate)
|
174
|
-
predicate_solns = graph.query([nil, predicate, nil])
|
175
|
-
urls = []
|
176
|
-
predicate_solns.each { |predicate_stmt |
|
177
|
-
predicate_obj = predicate_stmt.object
|
178
|
-
urls << predicate_obj.to_str if predicate_obj.is_a?(RDF::URI)
|
179
|
-
}
|
180
|
-
urls
|
181
|
-
end
|
182
|
-
|
183
|
-
# TODO: WRITE_COMMENTS_AND_TESTS_FOR_THIS_METHOD
|
184
|
-
# TODO: re-usable as part of Triannon::Graph class?
|
185
|
-
# @return [Array<String>] body chars as Strings, in an Array (one element for each contentAsText body)
|
186
|
-
def body_chars
|
187
|
-
result = []
|
188
|
-
q = RDF::Query.new
|
189
|
-
q << [nil, RDF::OpenAnnotation.hasBody, :body]
|
190
|
-
q << [:body, RDF.type, RDF::Content.ContentAsText]
|
191
|
-
q << [:body, RDF::Content.chars, :body_chars]
|
192
|
-
solns = graph.query q
|
193
|
-
solns.each { |soln|
|
194
|
-
result << soln.body_chars.value.strip
|
195
|
-
}
|
196
|
-
result
|
197
|
-
end
|
198
|
-
|
199
106
|
private
|
200
107
|
|
201
108
|
# loads RDF::Graph from data attribute. If data is in json-ld, converts it to turtle.
|
@@ -235,5 +142,9 @@ private
|
|
235
142
|
graph && graph.size > 0
|
236
143
|
end
|
237
144
|
|
145
|
+
def solr_writer
|
146
|
+
@sw ||= Triannon::SolrWriter.new
|
147
|
+
end
|
148
|
+
|
238
149
|
end
|
239
150
|
end
|
@@ -128,7 +128,7 @@ module Triannon
|
|
128
128
|
req.body = ttl
|
129
129
|
end
|
130
130
|
if resp.status != 200 && resp.status != 201
|
131
|
-
raise "Unable to create LDP resource in container #{
|
131
|
+
raise "Unable to create LDP resource in container #{parent_path}: Response Status: #{resp.status}\nResponse Body: #{resp.body}\nAnnotation sent: #{body}"
|
132
132
|
end
|
133
133
|
new_url = resp.headers['Location'] ? resp.headers['Location'] : resp.headers['location']
|
134
134
|
new_url.split('/').last if new_url
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Triannon
|
2
|
+
class SolrWriter
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@rsolr_client = RSolr.connect :url => Triannon.config[:solr_url]
|
6
|
+
@logger = Rails.logger
|
7
|
+
@max_retries = Triannon.config[:max_solr_retries] || 5
|
8
|
+
@base_sleep_seconds = Triannon.config[:base_sleep_seconds] || 1
|
9
|
+
@max_sleep_seconds = Triannon.config[:max_sleep_seconds] || 5
|
10
|
+
end
|
11
|
+
|
12
|
+
# Add the document to Solr, retrying if an error occurs.
|
13
|
+
# See https://github.com/ooyala/retries for info on with_retries.
|
14
|
+
# @param [Hash] doc a Hash representation of the solr document to be added
|
15
|
+
def add(doc)
|
16
|
+
id = doc[:id]
|
17
|
+
|
18
|
+
handler = Proc.new do |exception, attempt_cnt, total_delay|
|
19
|
+
@logger.debug "#{exception.inspect} on Solr add attempt #{attempt_cnt} for #{id}"
|
20
|
+
end
|
21
|
+
|
22
|
+
with_retries(:handler => handler,
|
23
|
+
:max_tries => @max_retries,
|
24
|
+
:base_sleep_seconds => @base_sleep_seconds,
|
25
|
+
:max_sleep_seconds => @max_sleep_seconds) do |attempt|
|
26
|
+
@logger.debug "Solr add attempt #{attempt} for #{id}"
|
27
|
+
# add it and commit within 0.5 seconds
|
28
|
+
@rsolr_client.add(doc, :add_attributes => {:commitWithin => 500})
|
29
|
+
@logger.info "Successfully indexed #{id} to Solr on attempt #{attempt}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Delete the document from Solr, retrying if an error occurs.
|
34
|
+
# See https://github.com/ooyala/retries for info on with_retries.
|
35
|
+
# @param [String] the id of the solr document to be deleted
|
36
|
+
def delete(id)
|
37
|
+
handler = Proc.new do |exception, attempt_cnt, total_delay|
|
38
|
+
@logger.debug "#{exception.inspect} on Solr delete attempt #{attempt_cnt} for #{id}"
|
39
|
+
end
|
40
|
+
|
41
|
+
with_retries(:handler => handler,
|
42
|
+
:max_tries => @max_retries,
|
43
|
+
:base_sleep_seconds => @base_sleep_seconds,
|
44
|
+
:max_sleep_seconds => @max_sleep_seconds) do |attempt|
|
45
|
+
@logger.debug "Solr delete attempt #{attempt} for #{id}"
|
46
|
+
@rsolr_client.delete_by_id(id)
|
47
|
+
@rsolr_client.commit
|
48
|
+
@logger.info "Successfully deleted #{id} from Solr"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" ?>
|
2
|
+
<!--
|
3
|
+
Licensed to the Apache Software Foundation (ASF) under one or more
|
4
|
+
contributor license agreements. See the NOTICE file distributed with
|
5
|
+
this work for additional information regarding copyright ownership.
|
6
|
+
The ASF licenses this file to You under the Apache License, Version 2.0
|
7
|
+
(the "License"); you may not use this file except in compliance with
|
8
|
+
the License. You may obtain a copy of the License at
|
9
|
+
|
10
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
|
12
|
+
Unless required by applicable law or agreed to in writing, software
|
13
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
See the License for the specific language governing permissions and
|
16
|
+
limitations under the License.
|
17
|
+
-->
|
18
|
+
|
19
|
+
<!--
|
20
|
+
All (relative) paths are relative to the installation path
|
21
|
+
|
22
|
+
persistent: Save changes made via the API to this file
|
23
|
+
sharedLib: path to a lib directory that will be shared across all cores
|
24
|
+
-->
|
25
|
+
<solr persistent="false" sharedLib="lib">
|
26
|
+
|
27
|
+
<!--
|
28
|
+
adminPath: RequestHandler path to manage cores.
|
29
|
+
If 'null' (or absent), cores will not be manageable via REST
|
30
|
+
-->
|
31
|
+
<cores adminPath="/admin/cores" defaultCoreName="development">
|
32
|
+
<core name="development" instanceDir="development-core" />
|
33
|
+
<core name="test" instanceDir="test-core" />
|
34
|
+
<core name="triannon" instanceDir="triannon-core" />
|
35
|
+
</cores>
|
36
|
+
</solr>
|
File without changes
|
@@ -190,7 +190,7 @@
|
|
190
190
|
<requestHandler name="/doc" class="solr.SearchHandler" >
|
191
191
|
<lst name="defaults">
|
192
192
|
<str name="echoParams">explicit</str>
|
193
|
-
<str name="fl"
|
193
|
+
<str name="fl">*</str>
|
194
194
|
<int name="rows">1</int>
|
195
195
|
<str name="q">{!raw f=id v=$id}</str> <!-- use id=666 instead of q=id:666 -->
|
196
196
|
<str name="wt">ruby</str>
|
data/config/triannon.yml
CHANGED
@@ -1,6 +1,15 @@
|
|
1
1
|
development:
|
2
|
-
ldp_url:
|
2
|
+
ldp_url: yer_ldp_store_url_here
|
3
|
+
solr_url: yer_triannon_solr_url_here
|
4
|
+
triannon_base_url: http://your.triannon-server.com/annotations/
|
5
|
+
max_solr_retries: 5
|
6
|
+
base_sleep_seconds: 1
|
7
|
+
max_sleep_seconds: 5
|
3
8
|
test: &test
|
4
9
|
ldp_url: yer_ldp_url_here
|
10
|
+
solr_url: yer_triannon_solr_url_here
|
11
|
+
triannon_base_url: http://your.triannon-server.com/annotations/
|
5
12
|
production:
|
6
|
-
ldp_url: yer_ldp_url_here
|
13
|
+
ldp_url: yer_ldp_url_here
|
14
|
+
solr_url: yer_triannon_solr_url_here
|
15
|
+
triannon_base_url: http://your.triannon-server.com/annotations/
|
@@ -10,12 +10,18 @@ module Triannon
|
|
10
10
|
default_yml =<<-YML
|
11
11
|
development:
|
12
12
|
ldp_url: http://localhost:8983/fedora/rest/anno
|
13
|
-
|
13
|
+
solr_url: http://localhost:8983/solr/triannon
|
14
|
+
triannon_base_url: http://your.triannon-server.com/annotations/
|
15
|
+
max_solr_retries: 5
|
16
|
+
base_sleep_seconds: 1
|
17
|
+
max_sleep_seconds: 5
|
14
18
|
test: &test
|
15
19
|
ldp_url: http://localhost:8983/fedora/rest/anno
|
16
|
-
|
20
|
+
solr_url: http://localhost:8983/solr/triannon
|
21
|
+
triannon_base_url: http://your.triannon-server.com/annotations/
|
17
22
|
production:
|
18
23
|
ldp_url:
|
24
|
+
solr_url:
|
19
25
|
triannon_base_url:
|
20
26
|
YML
|
21
27
|
create_file 'config/triannon.yml', default_yml
|
data/lib/triannon.rb
CHANGED
@@ -5,7 +5,9 @@ require 'rdf/ldp'
|
|
5
5
|
require 'rdf/fcrepo4'
|
6
6
|
require 'rdf/triannon_vocab'
|
7
7
|
require 'bootstrap-sass'
|
8
|
-
require 'faraday'
|
8
|
+
require 'faraday' # for writing to LDP store
|
9
|
+
require 'rsolr'
|
10
|
+
require 'retries' # for writing to Solr
|
9
11
|
|
10
12
|
module Triannon
|
11
13
|
require "triannon/engine"
|
data/lib/triannon/graph.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
module Triannon
|
2
2
|
# a wrapper class for RDF::Graph that adds methods specific to Triannon
|
3
|
+
# this is intended to be used for an RDF::Graph of a single annotation
|
3
4
|
class Graph
|
4
5
|
|
6
|
+
# Class Methods ----------------------------------------------------------------
|
7
|
+
|
5
8
|
# given an RDF::Resource (an RDF::Node or RDF::URI), look for all the statements with that object
|
6
9
|
# as the subject, and recurse through the graph to find all descendant statements pertaining to the subject
|
7
10
|
# @param subject the RDF object to be used as the subject in the graph query. Should be an RDF::Node or RDF::URI
|
@@ -19,13 +22,139 @@ module Triannon
|
|
19
22
|
# @return [RDF::Query] query for a subject :s with type of RDF::OpenAnnotation.Annotation
|
20
23
|
def self.anno_query
|
21
24
|
q = RDF::Query.new
|
22
|
-
q << [:s, RDF.type, RDF::
|
25
|
+
q << [:s, RDF.type, RDF::OpenAnnotation.Annotation]
|
23
26
|
end
|
24
27
|
|
28
|
+
|
29
|
+
# Instance Methods ----------------------------------------------------------------
|
30
|
+
|
31
|
+
# instantiate this class for an RDF::Graph of a single annotation
|
25
32
|
def initialize(rdf_graph)
|
26
33
|
@graph = rdf_graph
|
27
34
|
end
|
28
35
|
|
36
|
+
# @return json-ld representation of graph with OpenAnnotation context as a url
|
37
|
+
def jsonld_oa
|
38
|
+
inline_context = @graph.dump(:jsonld, :context => Triannon::JsonldContext::OA_CONTEXT_URL)
|
39
|
+
hash_from_json = JSON.parse(inline_context)
|
40
|
+
hash_from_json["@context"] = Triannon::JsonldContext::OA_CONTEXT_URL
|
41
|
+
hash_from_json.to_json
|
42
|
+
end
|
43
|
+
|
44
|
+
# @return json-ld representation of graph with IIIF context as a url
|
45
|
+
def jsonld_iiif
|
46
|
+
inline_context = @graph.dump(:jsonld, :context => Triannon::JsonldContext::IIIF_CONTEXT_URL)
|
47
|
+
hash_from_json = JSON.parse(inline_context)
|
48
|
+
hash_from_json["@context"] = Triannon::JsonldContext::IIIF_CONTEXT_URL
|
49
|
+
hash_from_json.to_json
|
50
|
+
end
|
51
|
+
|
52
|
+
# NOTE: NEVER get here before anno is stored
|
53
|
+
# the graph should have an assigned url for the @id of the root; it shouldn't be a blank node
|
54
|
+
# @param [String] the Triannon id for this anno. Defaults to nil, in which case id_as_url will be used.
|
55
|
+
# @return [Hash] a hash to be written to Solr, populated appropriately
|
56
|
+
def solr_hash(triannon_id=nil)
|
57
|
+
doc_hash = {}
|
58
|
+
# chars in Solr/Lucene query syntax are a big pain in Solr id fields, so we only use
|
59
|
+
# the uuid portion of the Triannon anno id, not the full url
|
60
|
+
triannon_id ||= id_as_url
|
61
|
+
solr_id = triannon_id.sub(Triannon.config[:ldp_url], "")
|
62
|
+
doc_hash[:id] = solr_id.sub(/^\//, "")
|
63
|
+
|
64
|
+
# use short strings for motivation field
|
65
|
+
doc_hash[:motivation] = motivated_by.map { |m| m.sub(RDF::OpenAnnotation.to_s, "") }
|
66
|
+
|
67
|
+
# date field format: 1995-12-31T23:59:59Z; or w fractional seconds: 1995-12-31T23:59:59.999Z
|
68
|
+
if annotated_at
|
69
|
+
begin
|
70
|
+
dt = Time.parse(annotated_at)
|
71
|
+
doc_hash[:annotated_at] = dt.iso8601 if dt
|
72
|
+
rescue ArgumentError
|
73
|
+
# ignore invalid datestamps
|
74
|
+
end
|
75
|
+
end
|
76
|
+
# doc_hash[:annotated_by_stem] # not yet implemented
|
77
|
+
|
78
|
+
doc_hash[:target_url] = predicate_urls RDF::OpenAnnotation.hasTarget
|
79
|
+
# TODO: recognize more target types
|
80
|
+
doc_hash[:target_type] = ['external_URI'] if doc_hash[:target_url].size > 0
|
81
|
+
|
82
|
+
doc_hash[:body_url] = predicate_urls RDF::OpenAnnotation.hasBody
|
83
|
+
doc_hash[:body_type] = []
|
84
|
+
doc_hash[:body_type] << 'external_URI' if doc_hash[:body_url].size > 0
|
85
|
+
doc_hash[:body_chars_exact] = body_chars.map {|bc| bc.strip}
|
86
|
+
doc_hash[:body_type] << 'content_as_text' if doc_hash[:body_chars_exact].size > 0
|
87
|
+
doc_hash[:body_type] << 'no_body' if doc_hash[:body_type].size == 0
|
88
|
+
|
89
|
+
doc_hash[:anno_jsonld] = jsonld_oa
|
90
|
+
doc_hash
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
# Canned Query methods ----------------------------------------------------------------
|
95
|
+
|
96
|
+
# @return [String] the id of this annotation as a url string
|
97
|
+
def id_as_url
|
98
|
+
solution = @graph.query self.class.anno_query
|
99
|
+
if solution && solution.size == 1
|
100
|
+
solution.first.s.to_s
|
101
|
+
# TODO: raise exception if not a URI or missing?
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# @return [Array<String>] Array of urls expressing the OA motivated_by values
|
106
|
+
def motivated_by
|
107
|
+
motivations = []
|
108
|
+
q = self.class.anno_query.dup
|
109
|
+
q << [:s, RDF::OpenAnnotation.motivatedBy, :motivated_by]
|
110
|
+
solution = @graph.query q
|
111
|
+
if solution && solution.size > 0
|
112
|
+
solution.each {|res|
|
113
|
+
motivations << res.motivated_by.to_s
|
114
|
+
}
|
115
|
+
end
|
116
|
+
# TODO: raise exception if none? (validation)
|
117
|
+
motivations
|
118
|
+
end
|
119
|
+
|
120
|
+
# @param [RDF::URI] predicate either RDF::OpenAnnotation.hasTarget or RDF::OpenAnnotation.hasBody
|
121
|
+
# @return [Array<String>] urls for the predicate, as an Array of Strings
|
122
|
+
def predicate_urls(predicate)
|
123
|
+
urls = []
|
124
|
+
predicate_solns = @graph.query [nil, predicate, nil]
|
125
|
+
predicate_solns.each { |predicate_stmt |
|
126
|
+
predicate_obj = predicate_stmt.object
|
127
|
+
urls << predicate_obj.to_str.strip if predicate_obj.is_a?(RDF::URI)
|
128
|
+
}
|
129
|
+
urls
|
130
|
+
end
|
131
|
+
|
132
|
+
# For all bodies that are of type ContentAsText, get the characters as a single String in the returned Array.
|
133
|
+
# @return [Array<String>] body chars as Strings, in an Array (one element for each contentAsText body)
|
134
|
+
def body_chars
|
135
|
+
result = []
|
136
|
+
q = RDF::Query.new
|
137
|
+
q << [nil, RDF::OpenAnnotation.hasBody, :body]
|
138
|
+
q << [:body, RDF.type, RDF::Content.ContentAsText]
|
139
|
+
q << [:body, RDF::Content.chars, :body_chars]
|
140
|
+
solns = @graph.query q
|
141
|
+
solns.each { |soln|
|
142
|
+
result << soln.body_chars.value
|
143
|
+
}
|
144
|
+
result
|
145
|
+
end
|
146
|
+
|
147
|
+
# @return [String] The datetime from the annotatedAt property, or nil
|
148
|
+
def annotated_at
|
149
|
+
solution = @graph.query [nil, RDF::OpenAnnotation.annotatedAt, nil]
|
150
|
+
if solution && solution.size == 1
|
151
|
+
solution.first.object.to_s
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
|
156
|
+
# Changing the Graph ----------------------------------------------------------------
|
157
|
+
|
29
158
|
# remove all RDF::OpenAnnotation.hasBody and .hasTarget statements
|
30
159
|
# and any other statements associated with body and target objects,
|
31
160
|
# leaving all statements to be stored as part of base object in LDP store
|
@@ -59,10 +188,9 @@ module Triannon
|
|
59
188
|
# transform an outer blank node into a null relative URI
|
60
189
|
def make_null_relative_uri_out_of_blank_node
|
61
190
|
anno_stmts = @graph.query([nil, RDF.type, RDF::OpenAnnotation.Annotation])
|
62
|
-
# FIXME: should actually look for subject with type of RDF::OpenAnnotation.Annotation
|
63
191
|
anno_rdf_obj = anno_stmts.first.subject
|
64
192
|
if anno_rdf_obj.is_a?(RDF::Node)
|
65
|
-
#
|
193
|
+
# use null relative URI representation of blank node
|
66
194
|
anno_subject = RDF::URI.new
|
67
195
|
else # it's already a URI
|
68
196
|
anno_subject = anno_rdf_obj
|
data/lib/triannon/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: triannon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Beer
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2015-01-
|
13
|
+
date: 2015-01-31 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rails
|
@@ -124,6 +124,34 @@ dependencies:
|
|
124
124
|
- - ">="
|
125
125
|
- !ruby/object:Gem::Version
|
126
126
|
version: '0'
|
127
|
+
- !ruby/object:Gem::Dependency
|
128
|
+
name: rsolr
|
129
|
+
requirement: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
type: :runtime
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
138
|
+
- - ">="
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '0'
|
141
|
+
- !ruby/object:Gem::Dependency
|
142
|
+
name: retries
|
143
|
+
requirement: !ruby/object:Gem::Requirement
|
144
|
+
requirements:
|
145
|
+
- - ">="
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: '0'
|
148
|
+
type: :runtime
|
149
|
+
prerelease: false
|
150
|
+
version_requirements: !ruby/object:Gem::Requirement
|
151
|
+
requirements:
|
152
|
+
- - ">="
|
153
|
+
- !ruby/object:Gem::Version
|
154
|
+
version: '0'
|
127
155
|
- !ruby/object:Gem::Dependency
|
128
156
|
name: rspec
|
129
157
|
requirement: !ruby/object:Gem::Requirement
|
@@ -261,6 +289,7 @@ files:
|
|
261
289
|
- app/services/triannon/ldp_to_oa_mapper.rb
|
262
290
|
- app/services/triannon/ldp_writer.rb
|
263
291
|
- app/services/triannon/root_annotation_creator.rb
|
292
|
+
- app/services/triannon/solr_writer.rb
|
264
293
|
- app/views/layouts/triannon/application.html.erb
|
265
294
|
- app/views/triannon/annotations/_form.html.erb
|
266
295
|
- app/views/triannon/annotations/edit.html.erb
|
@@ -269,8 +298,9 @@ files:
|
|
269
298
|
- app/views/triannon/annotations/show.html.erb
|
270
299
|
- config/initializers/mime_types.rb
|
271
300
|
- config/routes.rb
|
272
|
-
- config/solr/
|
273
|
-
- config/solr/
|
301
|
+
- config/solr/solr.xml
|
302
|
+
- config/solr/triannon-core/conf/schema.xml
|
303
|
+
- config/solr/triannon-core/conf/solrconfig.xml
|
274
304
|
- config/triannon.yml
|
275
305
|
- lib/generators/triannon/install_generator.rb
|
276
306
|
- lib/rdf/triannon_vocab.rb
|