triannon 0.4.4 → 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 +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
|