triannon 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/app/controllers/triannon/annotations_controller.rb +6 -0
- data/app/models/triannon/annotation.rb +6 -1
- data/app/models/triannon/annotation_ldp.rb +8 -6
- data/app/models/triannon/graph_validator.rb +33 -0
- data/app/services/triannon/ldp_creator.rb +138 -132
- data/app/services/triannon/ldp_loader.rb +38 -18
- data/app/services/triannon/ldp_to_oa_mapper.rb +167 -22
- data/app/views/triannon/annotations/_form.html.erb +1 -1
- data/lib/triannon.rb +1 -0
- data/lib/triannon/error.rb +9 -0
- data/lib/triannon/version.rb +1 -1
- metadata +19 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 98b04d335bf8e2b24385cfeb4088d9c8db62c5c8
|
4
|
+
data.tar.gz: e10c1dc27cd105c7acb4fee6de77f8f51dddac9e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6f46652fb5c45b9b47be3374919eb4fd9bcf79ae50bf69059ee3896640990cedd11c499684185ea85d9befb6820c17ec8e6e84fc6425fbd7cef979dc57b6ef31
|
7
|
+
data.tar.gz: e3967493035299c17a6e7235f288e0b212738e94b9817806869fb4fb737bb6508e72c05607372718e6e84738ba85cca2d5597143ca723cf148457e2a003a6976
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
[![Build Status](https://travis-ci.org/sul-dlss/triannon.svg?branch=master)](https://travis-ci.org/sul-dlss/triannon) [![Dependency Status](https://gemnasium.com/sul-dlss/triannon.svg)](https://gemnasium.com/sul-dlss/triannon) [![Gem Version](https://badge.fury.io/rb/triannon.svg)](http://badge.fury.io/rb/triannon)
|
1
|
+
[![Build Status](https://travis-ci.org/sul-dlss/triannon.svg?branch=master)](https://travis-ci.org/sul-dlss/triannon) [![Coverage Status](https://coveralls.io/repos/sul-dlss/triannon/badge.png)](https://coveralls.io/r/sul-dlss/triannon) [![Dependency Status](https://gemnasium.com/sul-dlss/triannon.svg)](https://gemnasium.com/sul-dlss/triannon) [![Gem Version](https://badge.fury.io/rb/triannon.svg)](http://badge.fury.io/rb/triannon)
|
2
2
|
|
3
3
|
# Triannon
|
4
4
|
|
@@ -4,6 +4,7 @@ module Triannon
|
|
4
4
|
class AnnotationsController < ApplicationController
|
5
5
|
before_action :default_format_jsonld, only: [:show]
|
6
6
|
before_action :set_annotation, only: [:show, :edit, :update, :destroy]
|
7
|
+
rescue_from Triannon::ExternalReferenceError, with: :ext_ref_error
|
7
8
|
|
8
9
|
# GET /annotations/annotations
|
9
10
|
def index
|
@@ -95,5 +96,10 @@ module Triannon
|
|
95
96
|
end
|
96
97
|
end
|
97
98
|
end
|
99
|
+
|
100
|
+
# handle Triannon::ExternalReferenceError
|
101
|
+
def ext_ref_error(exception)
|
102
|
+
render plain: exception.message, status: 403
|
103
|
+
end
|
98
104
|
end
|
99
105
|
end
|
@@ -178,7 +178,12 @@ private
|
|
178
178
|
end
|
179
179
|
|
180
180
|
def json_oa_context
|
181
|
-
|
181
|
+
# FIXME: this is a terrible place to do this!!
|
182
|
+
if Rails.root.to_s.match(/internal/) # testing via engine_cart
|
183
|
+
@json_oa_context ||= File.read(Rails.root.join("..", "..", "lib", "triannon", "oa_context_20130208.json"))
|
184
|
+
else
|
185
|
+
@json_oa_context ||= File.read(Rails.root.join("lib", "triannon", "oa_context_20130208.json"))
|
186
|
+
end
|
182
187
|
end
|
183
188
|
|
184
189
|
def graph_exists?
|
@@ -1,4 +1,6 @@
|
|
1
1
|
module Triannon
|
2
|
+
# an LDP aware model of an Annotation -- basically, a shim between the OA notion of an annotation
|
3
|
+
# and the LDP storage.
|
2
4
|
class AnnotationLdp
|
3
5
|
|
4
6
|
# RDF::Graph object with all triples, including back end (e.g. LDP, Fedora)
|
@@ -8,9 +10,7 @@ module Triannon
|
|
8
10
|
|
9
11
|
# RDF::Graph without any back end (e.g. LDP, Fedora) triples
|
10
12
|
def stripped_graph
|
11
|
-
|
12
|
-
new_graph = RDF::LDP.remove_ldp_triples (RDF::FCRepo4.remove_fedora_triples(graph))
|
13
|
-
end
|
13
|
+
RDF::LDP.remove_ldp_triples (RDF::FCRepo4.remove_fedora_triples(graph))
|
14
14
|
end
|
15
15
|
|
16
16
|
def base_uri
|
@@ -39,9 +39,11 @@ module Triannon
|
|
39
39
|
}
|
40
40
|
result
|
41
41
|
end
|
42
|
-
|
43
|
-
|
44
|
-
|
42
|
+
|
43
|
+
# add the passed statements to #graph
|
44
|
+
# @param [Array<RDF::Statement>] statements an array of RDF statements to be loaded into the graph
|
45
|
+
def load_statements_into_graph statements
|
46
|
+
graph.insert(statements) if statements && statements.size > 0
|
45
47
|
end
|
46
48
|
|
47
49
|
private
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Triannon
|
2
|
+
class GraphValidator < ActiveModel::Validator
|
3
|
+
|
4
|
+
def validate(anno)
|
5
|
+
graph_exists? anno
|
6
|
+
graph = anno.graph
|
7
|
+
basic_solution = graph.query(self.basic_query)
|
8
|
+
unless basic_solution && basic_solution.size == 1
|
9
|
+
anno.errors[:data] << 'The oa:Annotation class MUST be associated with each Annotation.'
|
10
|
+
anno.errors[:data] << 'each Annotation MUST have at least one target'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def graph_exists?(anno)
|
15
|
+
graph = anno.graph
|
16
|
+
unless graph && graph.size > 0
|
17
|
+
anno.errors[:data] << 'Unable to create non-null graph'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# query for a subject with
|
22
|
+
# predicate RDF::OpenAnnotation.hasTarget
|
23
|
+
# type of RDF::OpenAnnotation.Annotation
|
24
|
+
def self.basic_query
|
25
|
+
@@basic_query ||= begin
|
26
|
+
RDF::Query.new
|
27
|
+
basic_query << [:s, RDF.type, RDF::URI("http://www.w3.org/ns/oa#Annotation")]
|
28
|
+
basic_query << [:s, RDF::OpenAnnotation.hasTarget, nil]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -7,24 +7,27 @@ module Triannon
|
|
7
7
|
# use LDP protocol to create the OpenAnnotation.Annotation in an RDF store
|
8
8
|
# @param [Triannon::Annotation] anno a Triannon::Annotation object, from which we use the graph
|
9
9
|
def self.create(anno)
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
if anno && anno.graph
|
11
|
+
# TODO: special case if the Annotation object already has an id --
|
12
|
+
# see https://github.com/sul-dlss/triannon/issues/84
|
13
|
+
result = Triannon::LdpCreator.new anno
|
14
|
+
result.create_base
|
13
15
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
bodies_solns = anno.graph.query([nil, RDF::OpenAnnotation.hasBody, nil])
|
17
|
+
if bodies_solns.size > 0
|
18
|
+
result.create_body_container
|
19
|
+
result.create_body_resources
|
20
|
+
end
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
targets_solns = anno.graph.query([nil, RDF::OpenAnnotation.hasTarget, nil])
|
23
|
+
# NOTE: Annotation is invalid if there are no target statements
|
24
|
+
if targets_solns.size > 0
|
25
|
+
result.create_target_container
|
26
|
+
result.create_target_resources
|
27
|
+
end
|
26
28
|
|
27
|
-
|
29
|
+
result.id
|
30
|
+
end
|
28
31
|
end
|
29
32
|
|
30
33
|
# given an RDF::Resource (an RDF::Node or RDF::URI), look for all the statements with that object
|
@@ -51,16 +54,20 @@ module Triannon
|
|
51
54
|
|
52
55
|
# POSTS a ttl representation of the LDP Annotation container to the LDP store
|
53
56
|
def create_base
|
54
|
-
|
55
|
-
|
57
|
+
if @anno.graph.query([nil, RDF::Triannon.externalReference, nil]).count > 0
|
58
|
+
raise Triannon::ExternalReferenceError, "Incoming annotations may not have http://triannon.stanford.edu/ns/externalReference as a predicate."
|
59
|
+
end
|
60
|
+
|
61
|
+
# TODO: special case if the Annotation object already has an id --
|
62
|
+
# see https://github.com/sul-dlss/triannon/issues/84
|
56
63
|
g = RDF::Graph.new
|
57
64
|
@anno.graph.each { |s|
|
58
65
|
g << s
|
59
66
|
}
|
60
67
|
|
61
|
-
#
|
68
|
+
# don't include the hasBody statements and any other statements associated with them
|
62
69
|
bodies_stmts = g.query([nil, RDF::OpenAnnotation.hasBody, nil])
|
63
|
-
bodies_stmts.each { |has_body_stmt
|
70
|
+
bodies_stmts.each { |has_body_stmt|
|
64
71
|
g.delete has_body_stmt
|
65
72
|
body_obj = has_body_stmt.object
|
66
73
|
Triannon::LdpCreator.subject_statements(body_obj, g).each { |s|
|
@@ -68,9 +75,9 @@ module Triannon
|
|
68
75
|
}
|
69
76
|
}
|
70
77
|
|
71
|
-
#
|
78
|
+
# don't include the hasTarget statements and any other statements associated with them
|
72
79
|
targets_stmts = g.query([nil, RDF::OpenAnnotation.hasTarget, nil])
|
73
|
-
targets_stmts.each { |has_target_stmt
|
80
|
+
targets_stmts.each { |has_target_stmt|
|
74
81
|
g.delete has_target_stmt
|
75
82
|
target_obj = has_target_stmt.object
|
76
83
|
Triannon::LdpCreator.subject_statements(target_obj, g).each { |s|
|
@@ -111,120 +118,12 @@ module Triannon
|
|
111
118
|
|
112
119
|
# create the body resources inside the (already created) body container
|
113
120
|
def create_body_resources
|
114
|
-
|
115
|
-
body_ids = []
|
116
|
-
bodies_solns.each { |has_body_stmt |
|
117
|
-
graph_for_resource = RDF::Graph.new
|
118
|
-
body_obj = has_body_stmt.object
|
119
|
-
if body_obj.is_a?(RDF::Node)
|
120
|
-
# we need to use the null relative URI representation of blank nodes to write to LDP
|
121
|
-
body_subject = RDF::URI.new
|
122
|
-
else
|
123
|
-
# it's already a URI, but we need to use the null relative URI representation so we can
|
124
|
-
# write out as a Triannon:externalRef property with the URL, and any addl props too.
|
125
|
-
if body_obj.to_str
|
126
|
-
body_subject = RDF::URI.new
|
127
|
-
graph_for_resource << RDF::Statement({:subject => body_subject,
|
128
|
-
:predicate => RDF::Triannon.externalReference,
|
129
|
-
:object => RDF::URI.new(body_obj.to_str)})
|
130
|
-
addl_stmts = @anno.graph.query([body_obj, nil, nil])
|
131
|
-
addl_stmts.each { |s|
|
132
|
-
graph_for_resource << RDF::Statement({:subject => body_subject,
|
133
|
-
:predicate => s.predicate,
|
134
|
-
:object => s.object})
|
135
|
-
}
|
136
|
-
else # it's already a null relative URI
|
137
|
-
body_subject = body_obj
|
138
|
-
end
|
139
|
-
end
|
140
|
-
# add statements with body_obj as the subject
|
141
|
-
Triannon::LdpCreator.subject_statements(body_obj, @anno.graph).each { |s|
|
142
|
-
if s.subject == body_obj
|
143
|
-
graph_for_resource << RDF::Statement({:subject => body_subject,
|
144
|
-
:predicate => s.predicate,
|
145
|
-
:object => s.object})
|
146
|
-
else
|
147
|
-
graph_for_resource << s
|
148
|
-
end
|
149
|
-
}
|
150
|
-
body_ids << create_resource(graph_for_resource.to_ttl, "#{@id}/b")
|
151
|
-
}
|
152
|
-
body_ids
|
121
|
+
create_resources_in_container RDF::OpenAnnotation.hasBody
|
153
122
|
end
|
154
|
-
|
123
|
+
|
155
124
|
# create the target resources inside the (already created) target container
|
156
125
|
def create_target_resources
|
157
|
-
|
158
|
-
target_ids = []
|
159
|
-
target_solns.each { |has_target_stmt |
|
160
|
-
graph_for_resource = RDF::Graph.new
|
161
|
-
target_obj = has_target_stmt.object
|
162
|
-
if target_obj.is_a?(RDF::Node)
|
163
|
-
# we need to use the null relative URI representation of blank nodes to write to LDP
|
164
|
-
target_subject = RDF::URI.new
|
165
|
-
else
|
166
|
-
# it's already a URI, but we need to use the null relative URI representation so we can
|
167
|
-
# write out as a Triannon:externalRef property with the URL, and any addl props too.
|
168
|
-
if target_obj.to_str
|
169
|
-
target_subject = RDF::URI.new
|
170
|
-
graph_for_resource << RDF::Statement({:subject => target_subject,
|
171
|
-
:predicate => RDF::Triannon.externalReference,
|
172
|
-
:object => RDF::URI.new(target_obj.to_str)})
|
173
|
-
addl_stmts = @anno.graph.query([target_obj, nil, nil])
|
174
|
-
addl_stmts.each { |s|
|
175
|
-
graph_for_resource << RDF::Statement({:subject => target_subject,
|
176
|
-
:predicate => s.predicate,
|
177
|
-
:object => s.object})
|
178
|
-
}
|
179
|
-
else # it's already a null relative URI
|
180
|
-
target_subject = target_obj
|
181
|
-
end
|
182
|
-
end
|
183
|
-
# add statements with target_obj as the subject
|
184
|
-
orig_source_objects = [] # the object URI nodes from targetObject, OA.hasSource, (uri) statements
|
185
|
-
Triannon::LdpCreator.subject_statements(target_obj, @anno.graph).each { |s|
|
186
|
-
if s.subject == target_obj
|
187
|
-
# deal with external references in hasSource statements (i.e. targetObject, OA.hasSource, (url) )
|
188
|
-
if s.predicate == RDF::OpenAnnotation.hasSource && s.object.is_a?(RDF::URI) && s.object.to_str
|
189
|
-
# we need to represent the source URL as an externalReference - use a hash URI
|
190
|
-
source_object = RDF::URI.new("#source")
|
191
|
-
orig_source_objects << s.object
|
192
|
-
graph_for_resource << RDF::Statement({:subject => source_object,
|
193
|
-
:predicate => RDF::Triannon.externalReference,
|
194
|
-
:object => RDF::URI.new(s.object.to_str)})
|
195
|
-
# and all of the source URL's addl props
|
196
|
-
Triannon::LdpCreator.subject_statements(s.object, @anno.graph).each { |ss|
|
197
|
-
if ss.subject == s.object
|
198
|
-
graph_for_resource << RDF::Statement({:subject => source_object,
|
199
|
-
:predicate => ss.predicate,
|
200
|
-
:object => ss.object})
|
201
|
-
else
|
202
|
-
graph_for_resource << ss
|
203
|
-
end
|
204
|
-
}
|
205
|
-
# add the targetObj, OA.hasSource, (hash URI representation) to graph
|
206
|
-
graph_for_resource << RDF::Statement({:subject => target_subject,
|
207
|
-
:predicate => s.predicate,
|
208
|
-
:object => source_object})
|
209
|
-
else
|
210
|
-
graph_for_resource << RDF::Statement({:subject => target_subject,
|
211
|
-
:predicate => s.predicate,
|
212
|
-
:object => s.object})
|
213
|
-
end
|
214
|
-
else
|
215
|
-
graph_for_resource << s
|
216
|
-
end
|
217
|
-
}
|
218
|
-
# make sure the graph we will write contains no extraneous statements about source URI
|
219
|
-
# now represented as hash URI (#source)
|
220
|
-
orig_source_objects.each { |rdf_uri_node|
|
221
|
-
Triannon::LdpCreator.subject_statements(rdf_uri_node, graph_for_resource).each { |s|
|
222
|
-
graph_for_resource.delete(s)
|
223
|
-
}
|
224
|
-
}
|
225
|
-
target_ids << create_resource(graph_for_resource.to_ttl, "#{@id}/t")
|
226
|
-
}
|
227
|
-
target_ids
|
126
|
+
create_resources_in_container RDF::OpenAnnotation.hasTarget
|
228
127
|
end
|
229
128
|
|
230
129
|
def conn
|
@@ -261,5 +160,112 @@ module Triannon
|
|
261
160
|
end
|
262
161
|
end
|
263
162
|
|
163
|
+
# create the target/body resources inside the (already created) target/body container
|
164
|
+
# @param [RDF::URI] predicate either RDF::OpenAnnotation.hasTarget or RDF::OpenAnnotation.hasBody
|
165
|
+
def create_resources_in_container(predicate)
|
166
|
+
predicate_solns = @anno.graph.query([nil, predicate, nil])
|
167
|
+
resource_ids = []
|
168
|
+
predicate_solns.each { |predicate_stmt |
|
169
|
+
graph_for_resource = RDF::Graph.new
|
170
|
+
predicate_obj = predicate_stmt.object
|
171
|
+
if predicate_obj.is_a?(RDF::Node)
|
172
|
+
# we need to use the null relative URI representation of blank nodes to write to LDP
|
173
|
+
predicate_subject = RDF::URI.new
|
174
|
+
else
|
175
|
+
# it's already a URI, but we need to use the null relative URI representation so we can
|
176
|
+
# write out as a Triannon:externalRef property with the URL, and any addl props too.
|
177
|
+
if predicate_obj.to_str
|
178
|
+
predicate_subject = RDF::URI.new
|
179
|
+
graph_for_resource << RDF::Statement({:subject => predicate_subject,
|
180
|
+
:predicate => RDF::Triannon.externalReference,
|
181
|
+
:object => RDF::URI.new(predicate_obj.to_str)})
|
182
|
+
addl_stmts = @anno.graph.query([predicate_obj, nil, nil])
|
183
|
+
addl_stmts.each { |s|
|
184
|
+
graph_for_resource << RDF::Statement({:subject => predicate_subject,
|
185
|
+
:predicate => s.predicate,
|
186
|
+
:object => s.object})
|
187
|
+
}
|
188
|
+
else # it's already a null relative URI
|
189
|
+
predicate_subject = predicate_obj
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# add statements with predicate_obj as the subject
|
194
|
+
orig_hash_uri_objs = [] # the orig URI objects from [targetObject, OA.hasSource/.default/.item, (uri)] statements
|
195
|
+
hash_uri_counter = 1
|
196
|
+
Triannon::LdpCreator.subject_statements(predicate_obj, @anno.graph).each { |s|
|
197
|
+
if s.subject == predicate_obj
|
198
|
+
# deal with any external URI references which may occur in:
|
199
|
+
# OA.hasSource (from SpecificResource), OA.default or OA.item (from Choice, Composite, List)
|
200
|
+
if s.object.is_a?(RDF::URI) && s.object.to_s
|
201
|
+
# do we need to represent the URL as an externalReference with hash URI
|
202
|
+
if s.predicate == RDF::OpenAnnotation.hasSource
|
203
|
+
hash_uri_str = "#source"
|
204
|
+
elsif s.predicate == RDF::OpenAnnotation.default
|
205
|
+
hash_uri_str = "#default"
|
206
|
+
elsif s.predicate == RDF::OpenAnnotation.item
|
207
|
+
hash_uri_str = "#item#{hash_uri_counter}"
|
208
|
+
hash_uri_counter = hash_uri_counter + 1
|
209
|
+
else
|
210
|
+
# we don't need to represent the object URI as an external ref
|
211
|
+
hash_uri_str = nil
|
212
|
+
graph_for_resource << RDF::Statement({:subject => predicate_subject,
|
213
|
+
:predicate => s.predicate,
|
214
|
+
:object => s.object})
|
215
|
+
end
|
216
|
+
|
217
|
+
if hash_uri_str
|
218
|
+
# represent the object URL as an external ref
|
219
|
+
new_hash_uri_obj = RDF::URI.new(hash_uri_str)
|
220
|
+
orig_hash_uri_obj = s.object
|
221
|
+
orig_hash_uri_objs << orig_hash_uri_obj
|
222
|
+
# add [targetObj, OA.hasSource/.default/.item, (hash URI)] triple to graph
|
223
|
+
graph_for_resource << RDF::Statement({:subject => predicate_subject,
|
224
|
+
:predicate => s.predicate,
|
225
|
+
:object => new_hash_uri_obj})
|
226
|
+
|
227
|
+
# add externalReference triple to graph
|
228
|
+
graph_for_resource << RDF::Statement({:subject => new_hash_uri_obj,
|
229
|
+
:predicate => RDF::Triannon.externalReference,
|
230
|
+
:object => RDF::URI.new(orig_hash_uri_obj.to_s)})
|
231
|
+
# and all of the orig URL's addl props
|
232
|
+
Triannon::LdpCreator.subject_statements(orig_hash_uri_obj, @anno.graph).each { |ss|
|
233
|
+
if ss.subject == orig_hash_uri_obj
|
234
|
+
graph_for_resource << RDF::Statement({:subject => new_hash_uri_obj,
|
235
|
+
:predicate => ss.predicate,
|
236
|
+
:object => ss.object})
|
237
|
+
else
|
238
|
+
graph_for_resource << ss
|
239
|
+
end
|
240
|
+
}
|
241
|
+
end
|
242
|
+
# NOTE: already dealt with case where there is no hash uri above
|
243
|
+
else
|
244
|
+
# s.object is not a URI, and subject = predicate_subject -- it may be a blank node
|
245
|
+
graph_for_resource << RDF::Statement({:subject => predicate_subject,
|
246
|
+
:predicate => s.predicate,
|
247
|
+
:object => s.object})
|
248
|
+
end
|
249
|
+
else
|
250
|
+
# s.subject != predicate_obj
|
251
|
+
graph_for_resource << s
|
252
|
+
end
|
253
|
+
}
|
254
|
+
# make sure the graph we will write contains no extraneous statements about URIs
|
255
|
+
# now represented as hash URIs
|
256
|
+
orig_hash_uri_objs.each { |uri_node|
|
257
|
+
Triannon::LdpCreator.subject_statements(uri_node, graph_for_resource).each { |s|
|
258
|
+
graph_for_resource.delete(s)
|
259
|
+
}
|
260
|
+
}
|
261
|
+
if (predicate == RDF::OpenAnnotation.hasTarget)
|
262
|
+
resource_ids << create_resource(graph_for_resource.to_ttl, "#{@id}/t")
|
263
|
+
else
|
264
|
+
resource_ids << create_resource(graph_for_resource.to_ttl, "#{@id}/b")
|
265
|
+
end
|
266
|
+
}
|
267
|
+
resource_ids
|
268
|
+
end
|
269
|
+
|
264
270
|
end
|
265
271
|
end
|
@@ -6,11 +6,11 @@ module Triannon
|
|
6
6
|
|
7
7
|
def self.load key
|
8
8
|
l = Triannon::LdpLoader.new key
|
9
|
-
l.
|
10
|
-
l.
|
11
|
-
l.
|
9
|
+
l.load_anno_container
|
10
|
+
l.load_bodies
|
11
|
+
l.load_targets
|
12
12
|
|
13
|
-
oa_graph = Triannon::LdpToOaMapper.ldp_to_oa l.
|
13
|
+
oa_graph = Triannon::LdpToOaMapper.ldp_to_oa l.ldp_annotation
|
14
14
|
oa_graph
|
15
15
|
end
|
16
16
|
|
@@ -19,29 +19,32 @@ module Triannon
|
|
19
19
|
l.find_all
|
20
20
|
end
|
21
21
|
|
22
|
-
attr_accessor :
|
22
|
+
attr_accessor :ldp_annotation
|
23
23
|
|
24
24
|
def initialize key = nil
|
25
25
|
@key = key
|
26
26
|
@base_uri = Triannon.config[:ldp_url]
|
27
|
-
@
|
27
|
+
@ldp_annotation = Triannon::AnnotationLdp.new
|
28
28
|
end
|
29
29
|
|
30
|
-
|
31
|
-
|
30
|
+
# load annotation container object into @ldp_annotation's (our Triannon::AnnotationLdp object) graph
|
31
|
+
def load_anno_container
|
32
|
+
load_object_into_annotation_graph(@key)
|
32
33
|
end
|
33
34
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
@
|
35
|
+
# load body objects into @ldp_annotation's (our Triannon::AnnotationLdp object) graph
|
36
|
+
def load_bodies
|
37
|
+
@ldp_annotation.body_uris.each { |body_uri|
|
38
|
+
body_obj_path = body_uri.to_s.split(@base_uri + '/').last
|
39
|
+
load_object_into_annotation_graph(body_obj_path)
|
38
40
|
}
|
39
41
|
end
|
40
42
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
@
|
43
|
+
# load target objects into @ldp_annotation's (our Triannon::AnnotationLdp object) graph
|
44
|
+
def load_targets
|
45
|
+
@ldp_annotation.target_uris.each { |target_uri|
|
46
|
+
target_obj_path = target_uri.to_s.split(@base_uri + '/').last
|
47
|
+
load_object_into_annotation_graph(target_obj_path)
|
45
48
|
}
|
46
49
|
end
|
47
50
|
|
@@ -64,19 +67,36 @@ module Triannon
|
|
64
67
|
|
65
68
|
protected
|
66
69
|
|
70
|
+
# given a path to the back end storage url, retrieve the object from storage and load
|
71
|
+
# the triples (except storage specific triples) into the graph for @ldp_annotation, our Triannon::AnnotationLdp object
|
72
|
+
# @param [String] path the path to the object, e.g. the pid, or pid/t/target_pid
|
73
|
+
def load_object_into_annotation_graph(path)
|
74
|
+
@ldp_annotation.load_statements_into_graph(statements_from_ttl_minus_fedora(get_ttl path))
|
75
|
+
end
|
76
|
+
|
77
|
+
# gets object from back end storage as turtle serialization
|
67
78
|
def get_ttl sub_path = nil
|
68
79
|
resp = conn.get do |req|
|
69
80
|
req.url "#{sub_path}" if sub_path
|
70
|
-
req.headers['Accept'] = '
|
81
|
+
req.headers['Accept'] = 'application/x-turtle'
|
71
82
|
end
|
72
83
|
resp.body
|
73
84
|
end
|
74
85
|
|
86
|
+
# turns turtle serialization into Array of RDF::Statements, removing fedora-specific triples
|
87
|
+
# (leaving LDP and OA triples)
|
88
|
+
# @param [String] ttl a String containing RDF serialized as turtle
|
89
|
+
# @return [Array<RDF::Statements>] the RDF statements represented in the ttl
|
90
|
+
def statements_from_ttl_minus_fedora ttl
|
91
|
+
# RDF::Turtle::Reader.new(ttl).statements.to_a
|
92
|
+
g = RDF::Graph.new.from_ttl(ttl)
|
93
|
+
RDF::FCRepo4.remove_fedora_triples(g).statements
|
94
|
+
end
|
95
|
+
|
75
96
|
def conn
|
76
97
|
@c ||= Faraday.new @base_uri
|
77
98
|
end
|
78
99
|
|
79
100
|
end
|
80
101
|
|
81
|
-
|
82
102
|
end
|
@@ -5,20 +5,21 @@ module Triannon
|
|
5
5
|
def self.ldp_to_oa ldp_anno
|
6
6
|
mapper = Triannon::LdpToOaMapper.new ldp_anno
|
7
7
|
mapper.extract_base
|
8
|
-
mapper.
|
9
|
-
mapper.
|
8
|
+
mapper.extract_bodies
|
9
|
+
mapper.extract_targets
|
10
10
|
mapper.oa_graph
|
11
11
|
end
|
12
12
|
|
13
13
|
attr_accessor :id, :oa_graph
|
14
14
|
|
15
15
|
def initialize ldp_anno
|
16
|
-
@
|
16
|
+
@ldp_anno = ldp_anno
|
17
|
+
@ldp_anno_graph = ldp_anno.stripped_graph
|
17
18
|
@oa_graph = RDF::Graph.new
|
18
19
|
end
|
19
20
|
|
20
21
|
def extract_base
|
21
|
-
@
|
22
|
+
@ldp_anno_graph.each_statement do |stmnt|
|
22
23
|
if stmnt.predicate == RDF.type && stmnt.object == RDF::OpenAnnotation.Annotation
|
23
24
|
@id = stmnt.subject.to_s.split('/').last
|
24
25
|
@root_uri = RDF::URI.new(Triannon.config[:triannon_base_url] + "/#{@id}")
|
@@ -30,31 +31,175 @@ module Triannon
|
|
30
31
|
end
|
31
32
|
end
|
32
33
|
|
33
|
-
def
|
34
|
-
@
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
@oa_graph << [body_node, RDF.type, RDF::Content.ContentAsText]
|
40
|
-
@oa_graph << [body_node, RDF.type, RDF::DCMIType.Text]
|
41
|
-
res_chars = @ldp.stripped_graph.query [body_uri, RDF::Content.chars, nil]
|
42
|
-
if res_chars.count > 0
|
43
|
-
@oa_graph << [body_node, RDF::Content.chars, res_chars.first.object]
|
44
|
-
end
|
34
|
+
def extract_bodies
|
35
|
+
@ldp_anno.body_uris.each { |body_uri|
|
36
|
+
if !map_external_ref(body_uri, RDF::OpenAnnotation.hasBody) &&
|
37
|
+
!map_content_as_text(body_uri, RDF::OpenAnnotation.hasBody) &&
|
38
|
+
!map_specific_resource(body_uri, RDF::OpenAnnotation.hasBody)
|
39
|
+
map_choice(body_uri, RDF::OpenAnnotation.hasBody)
|
45
40
|
end
|
46
41
|
}
|
47
42
|
end
|
48
43
|
|
49
|
-
def
|
50
|
-
@
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
@oa_graph << [@root_uri, RDF::OpenAnnotation.hasTarget, ext_uri]
|
44
|
+
def extract_targets
|
45
|
+
@ldp_anno.target_uris.each { |target_uri|
|
46
|
+
if !map_external_ref(target_uri, RDF::OpenAnnotation.hasTarget) &&
|
47
|
+
!map_specific_resource(target_uri, RDF::OpenAnnotation.hasTarget)
|
48
|
+
map_choice(target_uri, RDF::OpenAnnotation.hasTarget)
|
55
49
|
end
|
56
50
|
}
|
57
51
|
end
|
52
|
+
|
53
|
+
# if uri_obj is the subject of a Triannon.externalReference then add appropriate
|
54
|
+
# statements to @oa_graph and return true
|
55
|
+
# @param [RDF::URI] uri_obj the object that may have RDF::Triannon.externalReference
|
56
|
+
# @param [RDF::URI] predicate the predicate for [subject_obj, predicate, (ext_url)] statement
|
57
|
+
# to be added to @oa_graph, e.g. RDF::OpenAnnotation.hasTarget
|
58
|
+
# @param [RDF::URI] the subject object to get the predicate statement; defaults to @root_uri
|
59
|
+
# @returns [Boolean] true if it adds statements to @oa_graph, false otherwise
|
60
|
+
def map_external_ref uri_obj, predicate, subject_obj = @root_uri
|
61
|
+
solns = @ldp_anno_graph.query [uri_obj, RDF::Triannon.externalReference, nil]
|
62
|
+
if solns.count > 0
|
63
|
+
external_uri = solns.first.object
|
64
|
+
@oa_graph << [subject_obj, predicate, external_uri]
|
65
|
+
|
66
|
+
Triannon::LdpCreator.subject_statements(uri_obj, @ldp_anno_graph).each { |stmt|
|
67
|
+
if stmt.subject == uri_obj && stmt.predicate != RDF::Triannon.externalReference
|
68
|
+
@oa_graph << [external_uri, stmt.predicate, stmt.object]
|
69
|
+
else
|
70
|
+
# we should never get here for external references ...
|
71
|
+
end
|
72
|
+
}
|
73
|
+
true
|
74
|
+
else
|
75
|
+
false
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# if uri_obj has a type of RDF::Content.ContentAsText, then this is a skolemized blank node;
|
80
|
+
# add appropriate statements to @oa_graph to represent the blank node and its contents and return true
|
81
|
+
# @param [RDF::URI] uri_obj the object that may type RDF::Content.ContentAsText
|
82
|
+
# @param [RDF::URI] predicate the predicate for [subject_obj, predicate, (ext_url)] statement
|
83
|
+
# to be added to @oa_graph, e.g. RDF::OpenAnnotation.hasTarget
|
84
|
+
# @param [RDF::URI] the subject object to get the predicate statement; defaults to @root_uri
|
85
|
+
# @returns [Boolean] true if it adds statements to @oa_graph, false otherwise
|
86
|
+
def map_content_as_text uri_obj, predicate, subject_obj = @root_uri
|
87
|
+
solns = @ldp_anno_graph.query [uri_obj, RDF.type, RDF::Content.ContentAsText]
|
88
|
+
if solns.count > 0
|
89
|
+
blank_node = RDF::Node.new
|
90
|
+
@oa_graph << [subject_obj, predicate, blank_node]
|
91
|
+
|
92
|
+
Triannon::LdpCreator.subject_statements(uri_obj, @ldp_anno_graph).each { |stmt|
|
93
|
+
if stmt.subject == uri_obj
|
94
|
+
@oa_graph << [blank_node, stmt.predicate, stmt.object]
|
95
|
+
else
|
96
|
+
# it is a descendant statment - take as is
|
97
|
+
@oa_graph << stmt
|
98
|
+
end
|
99
|
+
}
|
100
|
+
true
|
101
|
+
else
|
102
|
+
false
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# if uri_obj has a type of RDF::OpenAnnotation.SpecificResource, then this is a skolemized blank node;
|
107
|
+
# add appropriate statements to @oa_graph to represent the blank node and its contents and return true
|
108
|
+
# @param [RDF::URI] uri_obj the object that may have type RDF::OpenAnnotation.SpecificResource
|
109
|
+
# @param [RDF::URI] predicate the predicate for [@root_uri, predicate, (sel_res)] statement
|
110
|
+
# to be added to @oa_graph, e.g. RDF::OpenAnnotation.hasTarget
|
111
|
+
# @returns [Boolean] true if it adds statements to @oa_graph, false otherwise
|
112
|
+
def map_specific_resource uri_obj, predicate
|
113
|
+
solns = @ldp_anno_graph.query [uri_obj, RDF.type, RDF::OpenAnnotation.SpecificResource]
|
114
|
+
if solns.count > 0
|
115
|
+
blank_node = RDF::Node.new
|
116
|
+
@oa_graph << [@root_uri, predicate, blank_node]
|
117
|
+
|
118
|
+
source_obj = nil
|
119
|
+
selector_obj = nil
|
120
|
+
selector_blank_node = nil
|
121
|
+
specific_res_stmts = Triannon::LdpCreator.subject_statements(uri_obj, @ldp_anno_graph)
|
122
|
+
specific_res_stmts.each { |stmt|
|
123
|
+
if stmt.predicate == RDF::OpenAnnotation.hasSource
|
124
|
+
# expecting a hash URI
|
125
|
+
source_obj = stmt.object
|
126
|
+
if source_obj.to_s.match("#{uri_obj.to_s}#source")
|
127
|
+
source_has_ext_uri = map_external_ref source_obj, RDF::OpenAnnotation.hasSource, blank_node
|
128
|
+
end
|
129
|
+
elsif stmt.predicate == RDF::OpenAnnotation.hasSelector
|
130
|
+
# this becomes a blank node. Per http://www.openannotation.org/spec/core/specific.html#Selectors
|
131
|
+
# "Typically if all of the information needed to resolve the Selector (or other Specifier)
|
132
|
+
# is present within the graph, such as is the case for the
|
133
|
+
# FragmentSelector, TextQuoteSelector, TextPositionSelector and DataPositionSelector classes,
|
134
|
+
# then there is no need to have a resolvable resource that provides the same information."
|
135
|
+
selector_obj = stmt.object
|
136
|
+
selector_blank_node = RDF::Node.new
|
137
|
+
@oa_graph << [blank_node, RDF::OpenAnnotation.hasSelector, selector_blank_node]
|
138
|
+
end
|
139
|
+
}
|
140
|
+
|
141
|
+
# We can't know we'll hit hasSource and hasSelector statements in graph first,
|
142
|
+
# so we must do another pass through the statements to get that information
|
143
|
+
specific_res_stmts.each { |stmt|
|
144
|
+
if stmt.subject == uri_obj && stmt.object != source_obj && stmt.object != selector_obj
|
145
|
+
@oa_graph << [blank_node, stmt.predicate, stmt.object]
|
146
|
+
elsif stmt.subject != source_obj
|
147
|
+
if selector_blank_node && stmt.subject == selector_obj
|
148
|
+
@oa_graph << [selector_blank_node, stmt.predicate, stmt.object]
|
149
|
+
end
|
150
|
+
# there shouldn't be any other statements present
|
151
|
+
end
|
152
|
+
}
|
153
|
+
true
|
154
|
+
else
|
155
|
+
false
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# if uri_obj has a type of RDF::OpenAnnotation.Choice, then this is a skolemized blank node;
|
160
|
+
# add appropriate statements to @oa_graph to represent the blank node and its contents and return true
|
161
|
+
# @param [RDF::URI] uri_obj the object that may have type RDF::OpenAnnotation.Choice
|
162
|
+
# @param [RDF::URI] predicate the predicate for [@root_uri, predicate, (choice)] statement
|
163
|
+
# to be added to @oa_graph, e.g. RDF::OpenAnnotation.hasTarget
|
164
|
+
# @returns [Boolean] true if it adds statements to @oa_graph, false otherwise
|
165
|
+
def map_choice uri_obj, predicate
|
166
|
+
solns = @ldp_anno_graph.query [uri_obj, RDF.type, RDF::OpenAnnotation.Choice]
|
167
|
+
if solns.count > 0
|
168
|
+
blank_node = RDF::Node.new
|
169
|
+
@oa_graph << [@root_uri, predicate, blank_node]
|
170
|
+
|
171
|
+
default_obj = nil
|
172
|
+
item_objs = []
|
173
|
+
choice_stmts = Triannon::LdpCreator.subject_statements(uri_obj, @ldp_anno_graph)
|
174
|
+
choice_stmts.each { |stmt|
|
175
|
+
if stmt.predicate == RDF::OpenAnnotation.default
|
176
|
+
default_obj = stmt.object
|
177
|
+
# assume it is either ContentAsText or external ref
|
178
|
+
if !map_content_as_text(default_obj, RDF::OpenAnnotation.default, blank_node)
|
179
|
+
map_external_ref(default_obj, RDF::OpenAnnotation.default, blank_node)
|
180
|
+
end
|
181
|
+
elsif stmt.predicate == RDF::OpenAnnotation.item
|
182
|
+
item_objs << stmt.object
|
183
|
+
# assume it is either ContentAsText or external ref
|
184
|
+
if !map_content_as_text(stmt.object, RDF::OpenAnnotation.item, blank_node)
|
185
|
+
map_external_ref(stmt.object, RDF::OpenAnnotation.item, blank_node)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
}
|
189
|
+
|
190
|
+
# We can't know we'll hit item and default statements in graph first,
|
191
|
+
# so we must do another pass through the statements to get that information
|
192
|
+
choice_stmts.each { |stmt|
|
193
|
+
if stmt.subject == uri_obj && stmt.object != default_obj && !item_objs.include?(stmt.object)
|
194
|
+
@oa_graph << [blank_node, stmt.predicate, stmt.object]
|
195
|
+
# there shouldn't be any other unmapped statements present
|
196
|
+
end
|
197
|
+
}
|
198
|
+
true
|
199
|
+
else
|
200
|
+
false
|
201
|
+
end
|
202
|
+
end
|
58
203
|
|
59
204
|
end
|
60
205
|
|
data/lib/triannon.rb
CHANGED
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.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Beer
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-
|
12
|
+
date: 2014-11-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -221,6 +221,20 @@ dependencies:
|
|
221
221
|
- - ">="
|
222
222
|
- !ruby/object:Gem::Version
|
223
223
|
version: '0'
|
224
|
+
- !ruby/object:Gem::Dependency
|
225
|
+
name: coveralls
|
226
|
+
requirement: !ruby/object:Gem::Requirement
|
227
|
+
requirements:
|
228
|
+
- - ">="
|
229
|
+
- !ruby/object:Gem::Version
|
230
|
+
version: '0'
|
231
|
+
type: :development
|
232
|
+
prerelease: false
|
233
|
+
version_requirements: !ruby/object:Gem::Requirement
|
234
|
+
requirements:
|
235
|
+
- - ">="
|
236
|
+
- !ruby/object:Gem::Version
|
237
|
+
version: '0'
|
224
238
|
description:
|
225
239
|
email:
|
226
240
|
- cabeer@stanford.edu
|
@@ -241,6 +255,7 @@ files:
|
|
241
255
|
- app/helpers/triannon/application_helper.rb
|
242
256
|
- app/models/triannon/annotation.rb
|
243
257
|
- app/models/triannon/annotation_ldp.rb
|
258
|
+
- app/models/triannon/graph_validator.rb
|
244
259
|
- app/services/triannon/ldp_creator.rb
|
245
260
|
- app/services/triannon/ldp_destroyer.rb
|
246
261
|
- app/services/triannon/ldp_loader.rb
|
@@ -261,6 +276,7 @@ files:
|
|
261
276
|
- lib/tasks/triannon_tasks.rake
|
262
277
|
- lib/triannon.rb
|
263
278
|
- lib/triannon/engine.rb
|
279
|
+
- lib/triannon/error.rb
|
264
280
|
- lib/triannon/oa_context_20130208.json
|
265
281
|
- lib/triannon/version.rb
|
266
282
|
homepage:
|
@@ -283,7 +299,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
283
299
|
version: '0'
|
284
300
|
requirements: []
|
285
301
|
rubyforge_project:
|
286
|
-
rubygems_version: 2.
|
302
|
+
rubygems_version: 2.4.3
|
287
303
|
signing_key:
|
288
304
|
specification_version: 4
|
289
305
|
summary: Rails engine for working with storage of OpenAnnotations stored in Fedora4
|