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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f2328d724cb403823e2e7e9fbf5565a1244c1453
4
- data.tar.gz: 2424be58316b9d29eb4f8e48eee1ea1914a9b07c
3
+ metadata.gz: 9061e8fc2e0127540ae750118289a5f79a7f6969
4
+ data.tar.gz: 2785857023cdd9a5a2e5caa3ecb7810f613430cb
5
5
  SHA512:
6
- metadata.gz: 7de9938713c92a1c0e654ee71f44bfe558429e7ab2907a8a89752c12520e22e7bece73db6a54106f70e944075b346eb66fefe19c7317f4d7c60198cb6f5ce1a6
7
- data.tar.gz: dd44726e702079259c81c29ad89c19e8f9597e0a9493a44041e4021fbba90b4bd9c0901fbfdb57efc4907726472b01fdb802126d1420eaabcbf2ff6d002c6bac
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
- # Configure config/triannon.yml as specified above
57
- $ vi config/triannon.yml
69
+ ##### Configure spec/internal/config/triannon.yml as specified above
70
+ ```console
71
+ $ vi spec/internal/config/triannon.yml
72
+ ```
58
73
 
59
- # Generate root annotations container
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 ||= data_to_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 graph with OpenAnnotation context as a url
73
+ # @return json-ld representation of anno with OpenAnnotation context as a url
70
74
  def jsonld_oa
71
- inline_context = graph.dump(:jsonld, :context => Triannon::JsonldContext::OA_CONTEXT_URL)
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 graph with IIIF context as a url
78
+ # @return json-ld representation of anno with IIIF context as a url
78
79
  def jsonld_iiif
79
- inline_context = graph.dump(:jsonld, :context => Triannon::JsonldContext::IIIF_CONTEXT_URL)
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
- # FIXME: this should be part of validation: RDF.type should be RDF::OpenAnnotation.Annotation
98
- # @return [String] should always be a string representation of RDF::OpenAnnotation.Annotation
99
- def type
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 uris expressing the OA motivated_by values
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
- # TODO: WRITE_COMMENTS_AND_TESTS_FOR_THIS_METHOD
95
+ # Add annotation to Solr as a Solr document
134
96
  def solr_save
135
- # puts "TO DO: send add to Solr (after save)"
136
- # pp solr_hash
97
+ # pass in id we got from LDP Store
98
+ solr_writer.add(graph.solr_hash(id))
137
99
  end
138
100
 
139
- # TODO: WRITE_COMMENTS_AND_TESTS_FOR_THIS_METHOD
101
+ # Delete annotation from Solr
140
102
  def solr_delete
141
- # puts "TO DO: send delete to Solr (after destroy)"
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 #{url}: Response Status: #{resp.status}\nResponse Body: #{resp.body}\nAnnotation sent: #{body}"
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
@@ -1,7 +1,7 @@
1
1
  <h1>Annotations</h1>
2
2
  <ul>
3
3
  <% @annotations.each do |a| %>
4
- <li><%= link_to a.url, annotation_path(a) %></li>
4
+ <li><%= link_to a.id_as_url, annotation_path(a) %></li>
5
5
  <% end %>
6
6
  </ul>
7
7
 
@@ -1,7 +1,7 @@
1
1
  <h1>Annotation</h1>
2
2
  <p>
3
3
  <strong>Url (@id):</strong>
4
- <%= @annotation.url %>
4
+ <%= @annotation.id_as_url %>
5
5
  </p>
6
6
  <p>
7
7
  <strong>Motivated By:</strong>
@@ -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>
@@ -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">anno_jsonld</str>
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: yer_ldp_url_here
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
- triannon_base_url: http://your.triannon-server.com
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
- triannon_base_url: http://your.triannon-server.com
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"
@@ -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::URI("http://www.w3.org/ns/oa#Annotation")]
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
- # we need to use the null relative URI representation of blank nodes to write to LDP
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
@@ -1,3 +1,3 @@
1
1
  module Triannon
2
- VERSION = "0.4.4"
2
+ VERSION = "0.5.0"
3
3
  end
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.4
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-28 00:00:00.000000000 Z
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/schema.xml
273
- - config/solr/solrconfig.xml
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