triannon 0.0.4 → 0.0.5
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 +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
|
-
[](https://travis-ci.org/sul-dlss/triannon) [](https://gemnasium.com/sul-dlss/triannon) [](http://badge.fury.io/rb/triannon)
|
1
|
+
[](https://travis-ci.org/sul-dlss/triannon) [](https://coveralls.io/r/sul-dlss/triannon) [](https://gemnasium.com/sul-dlss/triannon) [](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
|