annotations2triannon 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.env_example +3 -3
- data/annotations2triannon.gemspec +1 -1
- data/bin/dms.rb +7 -60
- data/lib/annotations2triannon/annotation_list.rb +1 -1
- data/lib/annotations2triannon/annotation_tracker.rb +151 -0
- data/lib/annotations2triannon/configuration.rb +24 -1
- data/lib/annotations2triannon/iiif_collection.rb +1 -2
- data/lib/annotations2triannon/manifest.rb +1 -2
- data/lib/annotations2triannon/open_annotation.rb +54 -26
- data/lib/requires.rb +4 -3
- data/spec/lib/annotations2triannon/configuration_spec.rb +114 -2
- data/spec/lib/annotations2triannon/open_annotation_spec.rb +101 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e6da8313a7eb7e9653bd04afc165aa32c943fa9c
|
4
|
+
data.tar.gz: aa993a96c3a0d55eb4285a418dcec875c1cd93e2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fe085e82fa5c59527a126a00c59247eb98106117ba67367b83cad3b9b4db9dcda2182c1f8ad56dd8366c9da815d6b596dc064585d5adf992a1e5db72f1121c78
|
7
|
+
data.tar.gz: 1497a34f9cea9f48b0c583fa9d2ece23b3c16d68215e0ba728ad2caaf94c9980b2083d773fd16d436aa5b56807d12f9c2a0f61bb7466844b8166cbbb2eb833d9
|
data/.env_example
CHANGED
@@ -10,6 +10,7 @@ export DEBUG=false
|
|
10
10
|
|
11
11
|
# limit the depth of IIIF navigation; useful for development
|
12
12
|
# or testing a new data source.
|
13
|
+
export ANNO_LIMIT_RANDOM=true # use 0..limit or random sampling?
|
13
14
|
export ANNO_LIMIT_MANIFESTS=0 # 0 is all of them
|
14
15
|
export ANNO_LIMIT_ANNOLISTS=0 # 0 is all of them
|
15
16
|
export ANNO_LIMIT_OPENANNOS=0 # 0 is all of them
|
@@ -21,9 +22,8 @@ export ANNO_LOG_FILE='log/annotations2triannon.log'
|
|
21
22
|
# http://rtomayko.github.io/rack-cache/storage
|
22
23
|
export RACK_CACHE_ENABLED=true
|
23
24
|
export RACK_CACHE_VERBOSE=false
|
24
|
-
export RACK_CACHE_METASTORE='
|
25
|
-
|
26
|
-
export RACK_CACHE_ENTITYSTORE='file:/tmp/cache/anno_body'
|
25
|
+
export RACK_CACHE_METASTORE='file:tmp/cache/anno_meta'
|
26
|
+
export RACK_CACHE_ENTITYSTORE='file:tmp/cache/anno_body'
|
27
27
|
|
28
28
|
# Configure the triannon service
|
29
29
|
export TRIANNON_LOG_FILE='log/triannon_client.log'
|
data/bin/dms.rb
CHANGED
@@ -59,67 +59,14 @@ end
|
|
59
59
|
# -----------------------------------------------------------------------
|
60
60
|
# Annotation tracking using a file
|
61
61
|
|
62
|
-
|
63
|
-
|
64
|
-
# persist the anno_tracking data to a file
|
65
|
-
# @param anno_data [Hash]
|
66
|
-
def anno_tracking_save(anno_data)
|
67
|
-
begin
|
68
|
-
dump_json(ANNO_TRACKING_FILE, anno_data)
|
69
|
-
puts "Annotation records updated in: #{ANNO_TRACKING_FILE}"
|
70
|
-
rescue
|
71
|
-
msg = "FAILURE to save annotation tracking file #{ANNO_TRACKING_FILE}"
|
72
|
-
CONFIG.logger.error(msg)
|
73
|
-
end
|
74
|
-
end
|
62
|
+
anno_file = 'dms_annotation_tracking.json'
|
63
|
+
anno_tracker = Annotations2triannon::AnnotationTracker.new(anno_file)
|
75
64
|
|
76
|
-
# retrieve the anno_tracking data from a file
|
77
|
-
# @returns anno_data [Hash]
|
78
|
-
def anno_tracking_load
|
79
|
-
data = {}
|
80
|
-
begin
|
81
|
-
if File.exists? ANNO_TRACKING_FILE
|
82
|
-
if File.size(ANNO_TRACKING_FILE).to_i > 0
|
83
|
-
data = JSON.parse( File.read(ANNO_TRACKING_FILE) )
|
84
|
-
end
|
85
|
-
end
|
86
|
-
rescue
|
87
|
-
msg = "FAILURE to load annotation tracking file #{ANNO_TRACKING_FILE}"
|
88
|
-
CONFIG.logger.error(msg)
|
89
|
-
end
|
90
|
-
return data
|
91
|
-
end
|
92
|
-
|
93
|
-
|
94
|
-
# -----------------------------------------------------------------------
|
95
65
|
# DELETE previous annotations on triannon
|
96
|
-
|
97
66
|
puts "\nAnnotation cleanup:"
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
anno_lists.each_pair do |anno_list_uri, anno_list|
|
102
|
-
anno_list.each do |anno_data|
|
103
|
-
anno_uris << RDF::URI.new(anno_data['uri'])
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
anno_ids = anno_uris.collect {|uri| tc.annotation_id(uri) }
|
108
|
-
unless anno_ids.empty?
|
109
|
-
# Find the intersection of the DMS annotations previously
|
110
|
-
# created in triannon and the current set of annotations in triannon.
|
111
|
-
graph = tc.get_annotations
|
112
|
-
uris = tc.annotation_uris(graph)
|
113
|
-
ids = uris.collect {|uri| tc.annotation_id(uri)}
|
114
|
-
annos_to_remove = anno_ids & ids # intersection of arrays
|
115
|
-
annos_to_remove.each do |id|
|
116
|
-
success = tc.delete_annotation(id)
|
117
|
-
CONFIG.logger.error("FAILURE to delete #{id}") unless success
|
118
|
-
end
|
119
|
-
end
|
120
|
-
# Clear the record of the saved annotations
|
121
|
-
anno_tracking_save({})
|
122
|
-
|
67
|
+
anno_tracker.delete_annotations
|
68
|
+
anno_tracker.archive
|
69
|
+
anno_tracker.save({})
|
123
70
|
|
124
71
|
# -----------------------------------------------------------------------
|
125
72
|
# Loading IIIF annotations from a collection
|
@@ -194,10 +141,10 @@ text_annotations.each_pair do |m,anno_lists|
|
|
194
141
|
CONFIG.logger.error("FAILURE to POST #{oa.id}")
|
195
142
|
end
|
196
143
|
end
|
197
|
-
|
144
|
+
anno_tracker.save(anno_tracking)
|
198
145
|
end
|
199
146
|
end
|
200
|
-
|
147
|
+
anno_tracker.save(anno_tracking)
|
201
148
|
|
202
149
|
|
203
150
|
|
@@ -23,7 +23,7 @@ module Annotations2triannon
|
|
23
23
|
return @open_annotations unless @open_annotations.nil?
|
24
24
|
begin
|
25
25
|
oa_graphs = collect_open_annotations
|
26
|
-
oa_graphs =
|
26
|
+
oa_graphs = @@config.array_sampler(oa_graphs, @@config.limit_openannos)
|
27
27
|
oa_graphs.collect {|oa| Annotations2triannon::OpenAnnotation.new(oa)}
|
28
28
|
rescue => e
|
29
29
|
binding.pry if @@config.debug
|
@@ -0,0 +1,151 @@
|
|
1
|
+
|
2
|
+
module Annotations2triannon
|
3
|
+
|
4
|
+
# Annotation tracking
|
5
|
+
class AnnotationTracker
|
6
|
+
|
7
|
+
attr_accessor :anno_file
|
8
|
+
attr_accessor :config
|
9
|
+
|
10
|
+
def initialize(file_name='anno_tracking.json')
|
11
|
+
@config = Annotations2triannon.configuration
|
12
|
+
@anno_file = File.join(@config.log_path, file_name)
|
13
|
+
FileUtils.touch @anno_file
|
14
|
+
end
|
15
|
+
|
16
|
+
# Save the current tracking file to an archive file tagged by timestamp
|
17
|
+
def archive
|
18
|
+
# Date and time of day for calendar date (basic)
|
19
|
+
# %Y%m%dT%H%M%S%z => 20071119T083748-0600
|
20
|
+
time_stamp = DateTime.now.strftime('%Y%m%dT%H%M%S%z')
|
21
|
+
ext = File.extname(@anno_file)
|
22
|
+
archive = @anno_file.sub(ext, "_#{time_stamp}#{ext}")
|
23
|
+
FileUtils.copy(@anno_file, archive)
|
24
|
+
end
|
25
|
+
|
26
|
+
# retrieve the anno_tracking data from a file
|
27
|
+
# @returns data [Hash]
|
28
|
+
def load
|
29
|
+
begin
|
30
|
+
json_load(@anno_file) || {}
|
31
|
+
rescue
|
32
|
+
msg = "FAILURE to load annotation tracking file #{@anno_file}"
|
33
|
+
@config.logger.error(msg)
|
34
|
+
{}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Retrieve the annotation URIs from an anno_tracking data file
|
39
|
+
# Assumes the annotation tracking data is a hash with a structure:
|
40
|
+
# {
|
41
|
+
# manifest_uri: [annotation_list, annotation_list, ]
|
42
|
+
# }
|
43
|
+
# where each annotation_list is a hash with a structure:
|
44
|
+
# {
|
45
|
+
# anno_list_uri: [annotations, annotations, ]
|
46
|
+
# }
|
47
|
+
# where annotations is an array of hashes, with a structure:
|
48
|
+
# {
|
49
|
+
# uri: uri,
|
50
|
+
# chars: body_content_chars
|
51
|
+
# }
|
52
|
+
# and the uri is a triannon annotation URI.
|
53
|
+
# @returns data [Hash]
|
54
|
+
# @returns uris [Array<RDF::URI>] An array of URIs to delete from triannon
|
55
|
+
def load_uris
|
56
|
+
uris = []
|
57
|
+
data = load
|
58
|
+
data.each_pair do |manifest_uri, anno_lists|
|
59
|
+
anno_lists.each_pair do |anno_list_uri, anno_list|
|
60
|
+
anno_list.each do |anno_data|
|
61
|
+
uris << RDF::URI.new(anno_data['uri'])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
uris
|
66
|
+
end
|
67
|
+
|
68
|
+
# persist the anno_tracking data to a file
|
69
|
+
# @param data [Hash]
|
70
|
+
# @return success [Boolean]
|
71
|
+
def save(data)
|
72
|
+
begin
|
73
|
+
json_save(@anno_file, data)
|
74
|
+
puts "Annotation records updated in: #{@anno_file}"
|
75
|
+
return true
|
76
|
+
rescue
|
77
|
+
msg = "FAILURE to save annotation tracking file #{@anno_file}"
|
78
|
+
@config.logger.error(msg)
|
79
|
+
return false
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# DELETE previous annotations loaded to triannon
|
84
|
+
# Accepts an input array of annotation URIs or finds them in the
|
85
|
+
# annotation tracking data and removes them from triannon (if they exist).
|
86
|
+
# Logs warnings or errors for annotations that do not exist or fail to DELETE.
|
87
|
+
# @parameter uris [Array<RDF::URI>] An array of URIs to delete from triannon
|
88
|
+
# @return status [Boolean]
|
89
|
+
def delete_annotations(uris=[])
|
90
|
+
raise ArgumentError, 'uris must be an Array<RDF::URI>' unless uris.instance_of? Array
|
91
|
+
tc = TriannonClient::TriannonClient.new
|
92
|
+
status = true
|
93
|
+
uris = load_uris if uris.empty?
|
94
|
+
anno_ids = uris.collect {|uri| tc.annotation_id(uri) }
|
95
|
+
# TODO: Enable intersection code below when a better set of annotations
|
96
|
+
# can be retrieved from triannon and/or Solr.
|
97
|
+
anno_ids.each do |id|
|
98
|
+
unless tc.delete_annotation(id)
|
99
|
+
@config.logger.error("FAILURE to delete #{id}")
|
100
|
+
status = false
|
101
|
+
end
|
102
|
+
end
|
103
|
+
return status
|
104
|
+
|
105
|
+
# Find the intersection of the annotation URIs and
|
106
|
+
# the current set of annotations in triannon.
|
107
|
+
# Note: the triannon /annotations response may be a limited subset of
|
108
|
+
# annotations that does not include any of the previously submitted
|
109
|
+
# annotation IDs. If there are some annotations in the intersection,
|
110
|
+
# we have to assume that they are all present and proceed to delete
|
111
|
+
# them all.
|
112
|
+
# TODO: Use Solr to get a better list of current annotation URIs
|
113
|
+
# graph = tc.get_annotations
|
114
|
+
# uris = tc.annotation_uris(graph)
|
115
|
+
# ids = uris.collect {|uri| tc.annotation_id(uri)}
|
116
|
+
# annos_to_remove = anno_ids & ids # intersection of arrays
|
117
|
+
# if annos_to_remove.empty?
|
118
|
+
# @config.logger.warn("annotations were not found in triannon.")
|
119
|
+
# end
|
120
|
+
# if annos_to_remove.length < anno_ids.length
|
121
|
+
# @config.logger.warn("annotations are not current in triannon.")
|
122
|
+
# end
|
123
|
+
# annos_to_remove.each do |id|
|
124
|
+
# unless tc.delete_annotation(id)
|
125
|
+
# @config.logger.error("FAILURE to delete #{id}")
|
126
|
+
# status = false
|
127
|
+
# end
|
128
|
+
# end
|
129
|
+
# return status
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
def json_save(filename, data)
|
136
|
+
File.open(filename,'w') do |f|
|
137
|
+
f.write(JSON.pretty_generate(data))
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def json_load(filename)
|
142
|
+
if File.exists? filename
|
143
|
+
if File.size(filename).to_i > 0
|
144
|
+
JSON.parse( File.read(filename) )
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
@@ -8,6 +8,7 @@ module Annotations2triannon
|
|
8
8
|
attr_reader :logger
|
9
9
|
|
10
10
|
attr_accessor :debug
|
11
|
+
attr_accessor :limit_random
|
11
12
|
attr_accessor :limit_manifests
|
12
13
|
attr_accessor :limit_annolists
|
13
14
|
attr_accessor :limit_openannos
|
@@ -18,7 +19,8 @@ module Annotations2triannon
|
|
18
19
|
@debug = env_boolean('DEBUG')
|
19
20
|
logger_init
|
20
21
|
|
21
|
-
# In development, enable options for
|
22
|
+
# In development, enable options for sampling the data
|
23
|
+
@limit_random = env_boolean('ANNO_LIMIT_RANDOM') # 0..limit or random sampling
|
22
24
|
@limit_manifests = ENV['ANNO_LIMIT_MANIFESTS'].to_i # 0 disables sampling
|
23
25
|
@limit_annolists = ENV['ANNO_LIMIT_ANNOLISTS'].to_i # 0 disables sampling
|
24
26
|
@limit_openannos = ENV['ANNO_LIMIT_OPENANNOS'].to_i # 0 disables sampling
|
@@ -27,6 +29,27 @@ module Annotations2triannon
|
|
27
29
|
redis_init
|
28
30
|
end
|
29
31
|
|
32
|
+
# Utility method for sampling annotation arrays, using either linear or
|
33
|
+
# random sampling of a subset of elements. The instance variable
|
34
|
+
# .limit_random is a configuration parameter that defines whether
|
35
|
+
# linear or random sampling is used.
|
36
|
+
# @param array [Array] An array to be sampled
|
37
|
+
# @param limit [Integer] The number of elements to sample
|
38
|
+
# @returns array [Array] A subset of the input array
|
39
|
+
def array_sampler(array, limit=0)
|
40
|
+
if limit > 0
|
41
|
+
if @limit_random
|
42
|
+
array.sample(limit)
|
43
|
+
else
|
44
|
+
limit = limit - 1
|
45
|
+
array[0..limit]
|
46
|
+
end
|
47
|
+
else
|
48
|
+
array
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
|
30
53
|
private
|
31
54
|
|
32
55
|
def env_boolean(var)
|
@@ -38,8 +38,7 @@ module Annotations2triannon
|
|
38
38
|
|
39
39
|
def manifest_uris(q)
|
40
40
|
uris = rdf.query(q).collect {|s| s.subject }
|
41
|
-
|
42
|
-
uris
|
41
|
+
@@config.array_sampler(uris, @@config.limit_manifests)
|
43
42
|
end
|
44
43
|
|
45
44
|
def query_iiif_manifests
|
@@ -76,8 +76,7 @@ module Annotations2triannon
|
|
76
76
|
|
77
77
|
def collect_annotation_list_uris(q)
|
78
78
|
uris = rdf.query(q).collect {|s| s.subject }
|
79
|
-
|
80
|
-
uris
|
79
|
+
@@config.array_sampler(uris, @@config.limit_annolists)
|
81
80
|
end
|
82
81
|
|
83
82
|
end
|
@@ -18,23 +18,24 @@ module Annotations2triannon
|
|
18
18
|
# @param id [UUID|URI|String] to identify an open annotation
|
19
19
|
def initialize(graph=RDF::Graph.new, id=nil)
|
20
20
|
@@agent ||= Annotations2triannon::AGENT
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
raise ArgumentError, 'graph must be an open annotation' unless is_annotation?
|
30
|
-
id.nil? ? @id = get_id : @id = id
|
31
|
-
end
|
21
|
+
set_graph(graph)
|
22
|
+
# The set_graph will set @graph and also set @id, using the
|
23
|
+
# graph subject with RDF.type of OA.Annotation. However, the
|
24
|
+
# id parameter for this init can override this default behavior,
|
25
|
+
# but it should be set after calling set_graph so it first has
|
26
|
+
# a chance to extract an ID from the graph. Once the @id is set,
|
27
|
+
# the set_graph method will not touch it again.
|
28
|
+
@id = parse_id(id)
|
32
29
|
end
|
33
30
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
31
|
+
# @see #parse_id
|
32
|
+
def id=(id)
|
33
|
+
@id = parse_id(id)
|
34
|
+
end
|
35
|
+
|
36
|
+
# @see #set_graph
|
37
|
+
def graph=(graph)
|
38
|
+
set_graph(graph)
|
38
39
|
end
|
39
40
|
|
40
41
|
# @return [boolean] true if RDF.type is OA.Annotation, with OA.hasBody and OA.hasTarget
|
@@ -90,21 +91,11 @@ module Annotations2triannon
|
|
90
91
|
end
|
91
92
|
|
92
93
|
def body_graph
|
93
|
-
return @body_graph unless @body_graph.nil?
|
94
94
|
g = RDF::Graph.new
|
95
95
|
hasBody.each do |b|
|
96
96
|
@graph.query( [b, :p, :o] ).each_statement {|s| g << s}
|
97
|
-
# if b.uri?
|
98
|
-
# begin
|
99
|
-
# b_resource = Resource.new(b)
|
100
|
-
# b_resource.rdf.each_statement {|s| g << s}
|
101
|
-
# rescue
|
102
|
-
# # Nothing to be done here; the Resource#rdf method
|
103
|
-
# # will log errors in RDF retrieval
|
104
|
-
# end
|
105
|
-
# end
|
106
97
|
end
|
107
|
-
|
98
|
+
g
|
108
99
|
end
|
109
100
|
|
110
101
|
def body_contentAsText
|
@@ -268,6 +259,43 @@ module Annotations2triannon
|
|
268
259
|
@graph.dump(:ttl, standard_prefixes: true)
|
269
260
|
end
|
270
261
|
|
262
|
+
private
|
263
|
+
|
264
|
+
# Sets the annotation ID as an RDF::URI from id, but if the id is nil, it
|
265
|
+
# will try to get the ID from the graph and, as a last resort, it will
|
266
|
+
# generate a unique ID using a UUID.
|
267
|
+
# @param id [URI|UUID|String] to identify an open annotation
|
268
|
+
def parse_id(id=nil)
|
269
|
+
raise ArgumentError, 'id cannot be an empty String' if (id.instance_of?(String) && id.empty?)
|
270
|
+
if id.nil?
|
271
|
+
# Try to get the ID from the graph
|
272
|
+
q = [nil, RDF.type, OA.Annotation]
|
273
|
+
id = @graph.query(q).collect {|s| s.subject }.first
|
274
|
+
else
|
275
|
+
# Try to parse the ID as an RDF::URI
|
276
|
+
id = RDF::URI.parse(id)
|
277
|
+
end
|
278
|
+
# As a last resort, assign a UUID so @id will not be nil; an alternative
|
279
|
+
# could be to assign a blank node, using RDF::Node.new; TODO: provide a
|
280
|
+
# general configuration option to use blank nodes for OA graphs.
|
281
|
+
id ||= RDF::URI.parse(UUID.generate)
|
282
|
+
end
|
283
|
+
|
284
|
+
# Sets the annotation graph as an RDF::Graph
|
285
|
+
# @param graph [RDF::Graph]
|
286
|
+
def set_graph(graph)
|
287
|
+
raise ArgumentError, 'graph must be RDF::Graph instance' unless graph.instance_of? RDF::Graph
|
288
|
+
@graph = graph
|
289
|
+
# The following code applies consequential rules to ensure the OA has an
|
290
|
+
# ID and that it is an OA graph. These rules should be invoked whenever
|
291
|
+
# the @graph is initialized or assigned by graph= accessor.
|
292
|
+
# Update the ID using the graph annotation URI, unless it is already set.
|
293
|
+
# The parse_id method depends on @graph to identify the graph ID.
|
294
|
+
@id ||= parse_id
|
295
|
+
# Ensure it's an open annotation; these methods depend on @id to be set.
|
296
|
+
insert_annotation unless is_annotation?
|
297
|
+
end
|
298
|
+
|
271
299
|
end
|
272
300
|
|
273
301
|
end
|
data/lib/requires.rb
CHANGED
@@ -10,7 +10,6 @@ require 'rest-client'
|
|
10
10
|
RestClient.proxy = ENV['http_proxy'] unless ENV['http_proxy'].nil?
|
11
11
|
RestClient.proxy = ENV['HTTP_PROXY'] unless ENV['HTTP_PROXY'].nil?
|
12
12
|
if ENV['RACK_CACHE_ENABLED'].to_s.upcase == 'TRUE'
|
13
|
-
require 'dalli'
|
14
13
|
require 'restclient/components'
|
15
14
|
require 'rack/cache'
|
16
15
|
# RestClient.enable Rack::CommonLogger
|
@@ -18,8 +17,9 @@ if ENV['RACK_CACHE_ENABLED'].to_s.upcase == 'TRUE'
|
|
18
17
|
# Enable the HTTP cache to store meta and entity data according
|
19
18
|
# to the env config values or the defaults given here. See
|
20
19
|
# http://rtomayko.github.io/rack-cache/configuration for available options.
|
21
|
-
metastore = ENV['RACK_CACHE_METASTORE'] || 'file
|
22
|
-
entitystore = ENV['RACK_CACHE_ENTITYSTORE'] || 'file
|
20
|
+
metastore = ENV['RACK_CACHE_METASTORE'] || 'file:tmp/cache/meta'
|
21
|
+
entitystore = ENV['RACK_CACHE_ENTITYSTORE'] || 'file:tmp/cache/body'
|
22
|
+
require 'dalli' if ((metastore =~ /memcache/) || (entitystore =~ /memcache/))
|
23
23
|
verbose = ENV['RACK_CACHE_VERBOSE'].to_s.upcase == 'TRUE' || false
|
24
24
|
RestClient.enable Rack::Cache,
|
25
25
|
:metastore => metastore, :entitystore => entitystore, :verbose => verbose
|
@@ -52,6 +52,7 @@ require_relative 'annotations2triannon/configuration'
|
|
52
52
|
require_relative 'annotations2triannon/resource'
|
53
53
|
require_relative 'annotations2triannon/manifest'
|
54
54
|
require_relative 'annotations2triannon/annotation_list'
|
55
|
+
require_relative 'annotations2triannon/annotation_tracker'
|
55
56
|
require_relative 'annotations2triannon/iiif_collection'
|
56
57
|
require_relative 'annotations2triannon/iiif_manifest'
|
57
58
|
require_relative 'annotations2triannon/iiif_annotation_list'
|
@@ -8,17 +8,129 @@ module Annotations2triannon
|
|
8
8
|
it 'default value is false' do
|
9
9
|
ENV['DEBUG'] = nil
|
10
10
|
config = Configuration.new
|
11
|
-
expect(config.debug).to
|
11
|
+
expect(config.debug).to be false
|
12
12
|
end
|
13
13
|
end
|
14
|
-
|
15
14
|
describe '#debug=' do
|
16
15
|
it 'can set value' do
|
16
|
+
ENV['DEBUG'] = nil
|
17
17
|
config = Configuration.new
|
18
|
+
expect(config.debug).to be false
|
18
19
|
config.debug = true
|
19
20
|
expect(config.debug).to be_truthy
|
20
21
|
end
|
21
22
|
end
|
22
23
|
|
24
|
+
describe '#limit_random' do
|
25
|
+
it 'default value is false' do
|
26
|
+
ENV['ANNO_LIMIT_RANDOM'] = nil
|
27
|
+
config = Configuration.new
|
28
|
+
expect(config.limit_random).to be false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
describe '#limit_random=' do
|
32
|
+
it 'can set value' do
|
33
|
+
ENV['ANNO_LIMIT_RANDOM'] = nil
|
34
|
+
config = Configuration.new
|
35
|
+
expect(config.limit_random).to be false
|
36
|
+
config.limit_random = true
|
37
|
+
expect(config.limit_random).to be true
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#limit_manifests' do
|
42
|
+
it 'default value is zero' do
|
43
|
+
ENV['ANNO_LIMIT_MANIFESTS'] = nil
|
44
|
+
config = Configuration.new
|
45
|
+
expect(config.limit_manifests).to eql(0)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
describe '#limit_manifests=' do
|
49
|
+
it 'can set value' do
|
50
|
+
ENV['ANNO_LIMIT_MANIFESTS'] = nil
|
51
|
+
config = Configuration.new
|
52
|
+
expect(config.limit_manifests).to eql(0)
|
53
|
+
config.limit_manifests = 10
|
54
|
+
expect(config.limit_manifests).to eql(10)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '#limit_annolists' do
|
59
|
+
it 'default value is zero' do
|
60
|
+
ENV['ANNO_LIMIT_ANNOLISTS'] = nil
|
61
|
+
config = Configuration.new
|
62
|
+
expect(config.limit_annolists).to eql(0)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
describe '#limit_annolists=' do
|
66
|
+
it 'can set value' do
|
67
|
+
ENV['ANNO_LIMIT_ANNOLISTS'] = nil
|
68
|
+
config = Configuration.new
|
69
|
+
expect(config.limit_annolists).to eql(0)
|
70
|
+
config.limit_annolists = 10
|
71
|
+
expect(config.limit_annolists).to eql(10)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#limit_openannos' do
|
76
|
+
it 'default value is zero' do
|
77
|
+
ENV['ANNO_LIMIT_OPENANNOS'] = nil
|
78
|
+
config = Configuration.new
|
79
|
+
expect(config.limit_openannos).to eql(0)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
describe '#limit_openannos=' do
|
83
|
+
it 'can set value' do
|
84
|
+
ENV['ANNO_LIMIT_OPENANNOS'] = nil
|
85
|
+
config = Configuration.new
|
86
|
+
expect(config.limit_openannos).to eql(0)
|
87
|
+
config.limit_openannos = 10
|
88
|
+
expect(config.limit_openannos).to eql(10)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe '#array_sampler' do
|
93
|
+
it 'returns input array when limit=0, regardless of sampling method' do
|
94
|
+
config = Configuration.new
|
95
|
+
array = [*0..10]
|
96
|
+
limit = 0
|
97
|
+
config.limit_random = false
|
98
|
+
expect(config.array_sampler(array,limit)).to eql(array)
|
99
|
+
config.limit_random = true
|
100
|
+
expect(config.array_sampler(array,limit)).to eql(array)
|
101
|
+
end
|
102
|
+
it 'returns array subset when limit>0, regardless of sampling method' do
|
103
|
+
config = Configuration.new
|
104
|
+
array = [*0..10]
|
105
|
+
limit = 5
|
106
|
+
config.limit_random = false
|
107
|
+
samples = config.array_sampler(array,limit)
|
108
|
+
expect(samples.length).to eql(limit)
|
109
|
+
expect(samples.length < array.length).to be true
|
110
|
+
config.limit_random = true
|
111
|
+
samples = config.array_sampler(array,limit)
|
112
|
+
expect(samples.length).to eql(limit)
|
113
|
+
expect(samples.length < array.length).to be true
|
114
|
+
end
|
115
|
+
it 'returns array[0..(limit-1)] when limit>0, without random sampling' do
|
116
|
+
config = Configuration.new
|
117
|
+
array = [*0..10]
|
118
|
+
limit = 5
|
119
|
+
config.limit_random = false
|
120
|
+
samples = config.array_sampler(array,limit)
|
121
|
+
expect(samples.length).to eql(limit)
|
122
|
+
expect(samples).to eql(array[0..(limit-1)])
|
123
|
+
end
|
124
|
+
it 'returns random array subset when limit>0, with random sampling' do
|
125
|
+
config = Configuration.new
|
126
|
+
array = [*0..100]
|
127
|
+
limit = 5
|
128
|
+
config.limit_random = true
|
129
|
+
samples = config.array_sampler(array,limit)
|
130
|
+
expect(samples.length).to eql(limit)
|
131
|
+
expect(samples).not_to eql(array[0..(limit-1)])
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
23
135
|
end
|
24
136
|
end
|
@@ -35,6 +35,107 @@ describe Annotations2triannon::OpenAnnotation do
|
|
35
35
|
}' )
|
36
36
|
}
|
37
37
|
|
38
|
+
|
39
|
+
describe 'has constants:' do
|
40
|
+
it 'CONTENT vocabulary for http://www.w3.org/2011/content#' do
|
41
|
+
const = Annotations2triannon::OpenAnnotation::CONTENT
|
42
|
+
expect(const).to eql RDF::Vocab::CNT
|
43
|
+
end
|
44
|
+
it 'OA vocabulary for http://www.w3.org/ns/oa#' do
|
45
|
+
const = Annotations2triannon::OpenAnnotation::OA
|
46
|
+
expect(const).to eql RDF::Vocab::OA
|
47
|
+
end
|
48
|
+
it 'IIIF_CONTEXT for http://iiif.io/api/presentation/2/context.json' do
|
49
|
+
const = Annotations2triannon::OpenAnnotation::IIIF_CONTEXT
|
50
|
+
expect(const).to be_instance_of String
|
51
|
+
expect(const).to include('http://iiif.io/api/presentation/2/context.json')
|
52
|
+
end
|
53
|
+
it 'OA_CONTEXT for http://www.w3.org/ns/oa.jsonld' do
|
54
|
+
const = Annotations2triannon::OpenAnnotation::OA_CONTEXT
|
55
|
+
expect(const).to be_instance_of String
|
56
|
+
expect(const).to include('http://www.w3.org/ns/oa.jsonld')
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context '#id' do
|
61
|
+
it 'returns an RDF::URI' do
|
62
|
+
expect(g1.id).to be_a RDF::URI
|
63
|
+
expect(g2.id).to be_a RDF::URI
|
64
|
+
expect(g3.id).to be_a RDF::URI
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context '#new' do
|
69
|
+
let(:g) { RDF::Graph.new }
|
70
|
+
context 'for default init' do
|
71
|
+
it 'sets the #id as an RDF::URI of a UUID' do
|
72
|
+
oa = Annotations2triannon::OpenAnnotation.new
|
73
|
+
expect(oa.id).to be_a RDF::URI
|
74
|
+
expect(UUID.validate(oa.id)).to be true
|
75
|
+
end
|
76
|
+
it 'sets the #graph as an RDF::Graph' do
|
77
|
+
oa = Annotations2triannon::OpenAnnotation.new
|
78
|
+
expect(oa.graph).to be_a RDF::Graph
|
79
|
+
end
|
80
|
+
it 'the #graph is an open annotation' do
|
81
|
+
oa = Annotations2triannon::OpenAnnotation.new
|
82
|
+
expect(oa.is_annotation?).to be true
|
83
|
+
end
|
84
|
+
end
|
85
|
+
context 'for init with an RDF::Graph' do
|
86
|
+
it 'accepts an empty RDF::Graph' do
|
87
|
+
oa = Annotations2triannon::OpenAnnotation.new(g)
|
88
|
+
expect(oa.graph).to be_a RDF::Graph
|
89
|
+
end
|
90
|
+
it 'ensures an empty RDF::Graph is an open annotation' do
|
91
|
+
oa = Annotations2triannon::OpenAnnotation.new(g)
|
92
|
+
expect(oa.is_annotation?).to be true
|
93
|
+
end
|
94
|
+
it 'ensures a graph with an RDF.type is also an open annotation' do
|
95
|
+
id = RDF::URI.parse(UUID.generate)
|
96
|
+
g << [id, RDF.type, RDF::Vocab::SKOS.Concept]
|
97
|
+
oa = Annotations2triannon::OpenAnnotation.new(g)
|
98
|
+
expect(oa.is_annotation?).to be true
|
99
|
+
end
|
100
|
+
end
|
101
|
+
context 'for init with an ID' do
|
102
|
+
it 'accepts a nil ID and sets the #id as an RDF::URI of a UUID' do
|
103
|
+
oa = Annotations2triannon::OpenAnnotation.new(g, nil)
|
104
|
+
expect(oa.id).to be_a RDF::URI
|
105
|
+
expect(UUID.validate(oa.id)).to be true
|
106
|
+
end
|
107
|
+
it 'accepts a String ID and parses it as an RDF::URI' do
|
108
|
+
id = 'abc'
|
109
|
+
oa = Annotations2triannon::OpenAnnotation.new(g, id)
|
110
|
+
expect(oa.id).to be_a RDF::URI
|
111
|
+
expect(oa.id).to eql RDF::URI.parse(id)
|
112
|
+
end
|
113
|
+
it 'accepts an RDF::URI and parses it as an RDF::URI' do
|
114
|
+
id = RDF::URI.parse('abc')
|
115
|
+
oa = Annotations2triannon::OpenAnnotation.new(g, id)
|
116
|
+
expect(oa.id).to be_a RDF::URI
|
117
|
+
expect(oa.id).to eql id
|
118
|
+
end
|
119
|
+
it 'accepts a UUID and parses it as an RDF::URI' do
|
120
|
+
id = UUID.generate
|
121
|
+
oa = Annotations2triannon::OpenAnnotation.new(g, id)
|
122
|
+
expect(oa.id).to be_a RDF::URI
|
123
|
+
expect(oa.id).to eql RDF::URI.parse(id)
|
124
|
+
end
|
125
|
+
it 'does NOT accept an empty String ID' do
|
126
|
+
expect{Annotations2triannon::OpenAnnotation.new(g, '')}.to raise_error
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context '#graph' do
|
132
|
+
it 'returns an RDF::Graph' do
|
133
|
+
expect(g1.graph).to be_a RDF::Graph
|
134
|
+
expect(g2.graph).to be_a RDF::Graph
|
135
|
+
expect(g3.graph).to be_a RDF::Graph
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
38
139
|
context '#annotatedBy' do
|
39
140
|
it 'returns an array' do
|
40
141
|
expect(g1.annotatedBy).to be_a Array
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: annotations2triannon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Darren Weber
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-05-
|
11
|
+
date: 2015-05-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -329,6 +329,7 @@ files:
|
|
329
329
|
- bin/revs_annotations2csv.sh
|
330
330
|
- lib/annotations2triannon.rb
|
331
331
|
- lib/annotations2triannon/annotation_list.rb
|
332
|
+
- lib/annotations2triannon/annotation_tracker.rb
|
332
333
|
- lib/annotations2triannon/configuration.rb
|
333
334
|
- lib/annotations2triannon/iiif_annotation_list.rb
|
334
335
|
- lib/annotations2triannon/iiif_collection.rb
|