annotot 0.2.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +71 -1
- data/app/assets/javascripts/annotot/annotot_endpoint.js +10 -8
- data/app/controllers/annotot/annotations_controller.rb +13 -3
- data/app/controllers/annotot/application_controller.rb +1 -1
- data/app/models/annotot/annotation.rb +12 -0
- data/app/views/annotot/annotations/pages.json.jbuilder +4 -0
- data/app/views/annotot/annotations/show.json.erb +1 -0
- data/config/routes.rb +2 -1
- data/lib/annotot/version.rb +1 -1
- data/lib/generators/annotot/install_generator.rb +5 -0
- data/lib/tasks/annotot_tasks.rake +59 -4
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f30142a82474f3723a5d0e041a7b26bce696b1aa975d6b4dc3da81b7cc89ad43
|
4
|
+
data.tar.gz: 1f3d9a4ebb62364cb8ca4a2eea465abc6e4a461b397a61949afe4380c0c2f200
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bfbefc765005d29ab2ec2810941692a93a2d02111701c86648ba9e08efd4b21ee6f639ebf6ea209a185777e7b8020d013b1244c34dedbaff4a3b48ee5dd39421
|
7
|
+
data.tar.gz: 0a93107a49bf745413b4f515e6ceb072d62159f9c5d47919bf615b7b22eef27e4e1af984cf10ea647a0664491552f5f58dbcd666562a0bd3dccf784bf7442059
|
data/README.md
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
|
4
4
|
Need to persist annotations quick and easily? Annotot, the original mini annotation API is for you. Don't annotate, annotot instead.
|
5
5
|
|
6
|
-
h/t @
|
6
|
+
h/t [@eefahy](https://github.com/eefahy) for the inspiration and name
|
7
7
|
|
8
8
|
![](annotot.png)
|
9
9
|
|
@@ -30,6 +30,76 @@ annotationEndpoint: {
|
|
30
30
|
|
31
31
|
If you want to configure Annotot to receive annotations from external sources make sure that you enable CORs in the Rails application. This can be done using [rack-cors](https://github.com/cyu/rack-cors).
|
32
32
|
|
33
|
+
## API
|
34
|
+
|
35
|
+
Annotot by default mounts itself at `/`. Though [this can be changed](http://guides.rubyonrails.org/engines.html#mounting-the-engine). All API endpoints are relative to its mount location.
|
36
|
+
|
37
|
+
| Path / Url | HTTP Verb | Path | Controller#Action |
|
38
|
+
| --- | --- | --- | --- |
|
39
|
+
| [annotations_path](#annotationspath) | GET | /annotations(.:format) | annotot/annotations#index {:format=>:json} |
|
40
|
+
| | POST | /annotations(.:format) | annotot/annotations#create {:format=>:json} |
|
41
|
+
| [lists_annotations_path](#listsannotationspath) | GET | /annotations/lists(.:format) | annotot/annotations#lists {:format=>:json} |
|
42
|
+
| [annotation_path](#annotationpath) | PATCH | /annotations/:id(.:format) | annotot/annotations#update {:format=>:json} |
|
43
|
+
| | PUT | /annotations/:id(.:format) | annotot/annotations#update {:format=>:json}
|
44
|
+
| | DELETE | /annotations/:id(.:format) | annotot/annotations#destroy {:format=>:json}
|
45
|
+
|
46
|
+
---
|
47
|
+
|
48
|
+
### annotations_path
|
49
|
+
`GET` - Return annotations for a given canvas
|
50
|
+
|
51
|
+
Parameters:
|
52
|
+
|
53
|
+
| Name | Required? | Description |
|
54
|
+
| --- | --- | --- |
|
55
|
+
| uri | yes | Canvas uri for which to return annotations
|
56
|
+
|
57
|
+
`POST` - Create a new annotation
|
58
|
+
|
59
|
+
Parameters:
|
60
|
+
|
61
|
+
| Name | Required? | Description |
|
62
|
+
| --- | --- | --- |
|
63
|
+
| annotation | yes | object containing creation parameters
|
64
|
+
| annotation.uuid | no | uuid for annotation
|
65
|
+
| annotation.data | no | annotation body data as string
|
66
|
+
| annotation.canvas | no | canvas to place the annotation on
|
67
|
+
|
68
|
+
---
|
69
|
+
|
70
|
+
### lists_annotations_path
|
71
|
+
`GET` - Return an AnnotationList of annotations for a given canvas
|
72
|
+
|
73
|
+
Parameters:
|
74
|
+
|
75
|
+
| Name | Required? | Description |
|
76
|
+
| --- | --- | --- |
|
77
|
+
| uri | yes | Canvas uri for which to return annotations
|
78
|
+
|
79
|
+
---
|
80
|
+
|
81
|
+
### annotation_path
|
82
|
+
`PATCH`, `PUT` - Update an annotation
|
83
|
+
|
84
|
+
Parameters:
|
85
|
+
|
86
|
+
| Name | Required? | Description |
|
87
|
+
| --- | --- | --- |
|
88
|
+
| id | yes | Canvas uri or Rails ActiveRecord id for annotation to update
|
89
|
+
| annotation | yes | object containing creation parameters
|
90
|
+
| annotation.uuid | no | uuid for annotation
|
91
|
+
| annotation.data | no | annotation body data as string
|
92
|
+
| annotation.canvas | no | canvas to place the annotation on
|
93
|
+
|
94
|
+
`DELETE` - Delete an annotation
|
95
|
+
|
96
|
+
Parameters:
|
97
|
+
|
98
|
+
| Name | Required? | Description |
|
99
|
+
| --- | --- | --- |
|
100
|
+
| id | yes | Canvas uri or Rails ActiveRecord id for annotation to delete
|
101
|
+
|
102
|
+
|
33
103
|
## Installation
|
34
104
|
Add this line to your application's Gemfile:
|
35
105
|
|
@@ -31,7 +31,7 @@
|
|
31
31
|
search: function(options, successCallback, errorCallback) {
|
32
32
|
var _this = this;
|
33
33
|
_this.annotationsList = [];
|
34
|
-
options.format = 'json'
|
34
|
+
options.format = 'json';
|
35
35
|
|
36
36
|
jQuery.ajax({
|
37
37
|
url: _this.endpoint,
|
@@ -65,7 +65,7 @@
|
|
65
65
|
deleteAnnotation: function(annotationID, successCallback, errorCallback) {
|
66
66
|
var _this = this;
|
67
67
|
jQuery.ajax({
|
68
|
-
url: _this.endpoint + '/' + annotationID,
|
68
|
+
url: _this.endpoint + '/' + encodeURIComponent(annotationID),
|
69
69
|
type: 'DELETE',
|
70
70
|
dataType: 'json',
|
71
71
|
headers: {
|
@@ -89,11 +89,11 @@
|
|
89
89
|
update: function(oaAnnotation, successCallback, errorCallback) {
|
90
90
|
var _this = this;
|
91
91
|
var annotation = _this.getAnnotationInEndpoint(oaAnnotation);
|
92
|
-
|
93
|
-
var annotationID = annotation.annotation
|
92
|
+
|
93
|
+
var annotationID = annotation.annotation.uuid;
|
94
94
|
|
95
95
|
jQuery.ajax({
|
96
|
-
url: _this.endpoint + '/' + annotationID,
|
96
|
+
url: _this.endpoint + '/' + encodeURIComponent(annotationID),
|
97
97
|
type: 'PATCH',
|
98
98
|
dataType: 'json',
|
99
99
|
headers: {
|
@@ -158,13 +158,15 @@
|
|
158
158
|
// Converts OA Annotation to endpoint format
|
159
159
|
getAnnotationInEndpoint: function(oaAnnotation) {
|
160
160
|
// Generate a new uuid if one is not present
|
161
|
-
|
161
|
+
// condition needs to be set to check for "falsy" values
|
162
|
+
// !oaAnnotation['@id'] should match null and undefined
|
163
|
+
if (!oaAnnotation['@id']) {
|
162
164
|
oaAnnotation["@id"] = Mirador.genUUID();
|
163
165
|
}
|
164
|
-
|
166
|
+
|
165
167
|
canvas = oaAnnotation.on[0].full;
|
166
168
|
var newAnno = jQuery.extend({}, oaAnnotation);
|
167
|
-
delete newAnno.endpoint
|
169
|
+
delete newAnno.endpoint;
|
168
170
|
return {
|
169
171
|
annotation: {
|
170
172
|
uuid: oaAnnotation["@id"],
|
@@ -2,18 +2,26 @@ require_dependency "annotot/application_controller"
|
|
2
2
|
|
3
3
|
module Annotot
|
4
4
|
class AnnotationsController < ApplicationController
|
5
|
-
before_action :set_annotation, only: %i[update destroy]
|
5
|
+
before_action :set_annotation, only: %i[update destroy show]
|
6
6
|
|
7
7
|
# GET /annotations
|
8
8
|
def index
|
9
9
|
@annotations = Annotation.where(canvas: annotation_search_params)
|
10
10
|
end
|
11
11
|
|
12
|
+
# GET /annotations
|
13
|
+
def show; end
|
14
|
+
|
12
15
|
# Get /annotations/lists
|
13
16
|
def lists
|
14
17
|
@annotations = Annotation.where(canvas: annotation_search_params)
|
15
18
|
end
|
16
19
|
|
20
|
+
# Get /annotations/pages
|
21
|
+
def pages
|
22
|
+
@annotations = Annotation.where(canvas: annotation_search_params)
|
23
|
+
end
|
24
|
+
|
17
25
|
# POST /annotations
|
18
26
|
def create
|
19
27
|
@annotation = Annotation.new(annotation_params)
|
@@ -50,7 +58,9 @@ module Annotot
|
|
50
58
|
private
|
51
59
|
|
52
60
|
def set_annotation
|
53
|
-
@annotation = Annotation.
|
61
|
+
@annotation = Annotot::Annotation.retrieve_by_id_or_uuid(
|
62
|
+
CGI.unescape(params[:id])
|
63
|
+
)
|
54
64
|
raise ActiveRecord::RecordNotFound unless @annotation.present?
|
55
65
|
end
|
56
66
|
|
@@ -59,7 +69,7 @@ module Annotot
|
|
59
69
|
end
|
60
70
|
|
61
71
|
def annotation_search_params
|
62
|
-
params.require(:uri)
|
72
|
+
CGI.unescape(params.require(:uri))
|
63
73
|
end
|
64
74
|
end
|
65
75
|
end
|
@@ -4,5 +4,17 @@ module Annotot
|
|
4
4
|
validates :canvas, presence: true
|
5
5
|
|
6
6
|
serialize :data, JSON
|
7
|
+
|
8
|
+
##
|
9
|
+
# Used to differentiate between a numeric id and a uuid. Rails will trim a
|
10
|
+
# uuid with leading numbers eg '123a'.to_i => 123
|
11
|
+
# @param [String] identifier
|
12
|
+
def self.retrieve_by_id_or_uuid(identifier)
|
13
|
+
if identifier =~ /\A\d+\z/
|
14
|
+
find_by(id: identifier)
|
15
|
+
else
|
16
|
+
find_by(uuid: identifier)
|
17
|
+
end
|
18
|
+
end
|
7
19
|
end
|
8
20
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= JSON.parse(@annotation.data).to_json.html_safe %>
|
data/config/routes.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
Annotot::Engine.routes.draw do
|
2
|
-
resources :annotations, except: %i[new edit
|
2
|
+
resources :annotations, path: '/', except: %i[new edit], defaults: { format: :json } do
|
3
3
|
collection do
|
4
4
|
get 'lists'
|
5
|
+
get 'pages'
|
5
6
|
end
|
6
7
|
end
|
7
8
|
end
|
data/lib/annotot/version.rb
CHANGED
@@ -1,4 +1,59 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
require 'json'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
namespace :annotot do
|
5
|
+
desc 'Import annotation list from path'
|
6
|
+
task :import_file, [:url_path] => :environment do |_t, args|
|
7
|
+
path = Pathname.new(args.url_path)
|
8
|
+
puts "Importing annotations from #{path}"
|
9
|
+
anno_list = JSON.parse(File.read(path))
|
10
|
+
puts "#{anno_list['resources'].length} resources found"
|
11
|
+
touch_count = 0
|
12
|
+
anno_list['resources'].map do |resource|
|
13
|
+
uuid = resource['@id']
|
14
|
+
selector = resource['on'].first['selector']
|
15
|
+
canvas = resource['on'].first['full']
|
16
|
+
|
17
|
+
##
|
18
|
+
# Annotation appears to be Mirador 2.6.0 compatible
|
19
|
+
if resource['on'].first['@type'] == 'oa:SpecificResource' && selector['@type'] == 'oa:Choice'
|
20
|
+
if selector['default']['@type'] == 'oa:FragmentSelector' && selector['item']['@type'] == 'oa:SvgSelector'
|
21
|
+
touch_count += 1
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
##
|
26
|
+
# Annotation needs to be updated to a single fragment selector
|
27
|
+
if resource['on'].first['@type'] == 'oa:SpecificResource' && selector['@type'] == 'oa:FragmentSelector'
|
28
|
+
new_on = "#{canvas}##{selector['value']}"
|
29
|
+
resource['on'] = new_on
|
30
|
+
touch_count += 1
|
31
|
+
end
|
32
|
+
|
33
|
+
##
|
34
|
+
# Annotation only has an SvgSelector and in an non-compliant format
|
35
|
+
if selector['@type'] == 'oa:SvgSelector'
|
36
|
+
item = selector.deep_dup
|
37
|
+
# Fix missing quotes in SVG
|
38
|
+
item['value'] = item['value'].gsub('xmlns=http://www.w3.org/2000/svg', 'xmlns="http://www.w3.org/2000/svg"')
|
39
|
+
selector['@type'] = 'oa:Choice'
|
40
|
+
selector['default'] = {
|
41
|
+
'@type' => 'oa:FragmentSelector',
|
42
|
+
'value' => 'xywh=0,0,0,0'
|
43
|
+
}
|
44
|
+
selector.delete('value')
|
45
|
+
selector['item'] = item
|
46
|
+
resource['on'].first['selector'] = selector.deep_dup
|
47
|
+
touch_count += 1
|
48
|
+
end
|
49
|
+
|
50
|
+
anno_json = resource.to_json
|
51
|
+
anno = Annotot::Annotation.find_or_create_by(uuid: uuid)
|
52
|
+
anno.update!(
|
53
|
+
canvas: canvas,
|
54
|
+
data: anno_json
|
55
|
+
)
|
56
|
+
end
|
57
|
+
puts "Updated #{touch_count} annotations"
|
58
|
+
end
|
59
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: annotot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jack Reed
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-07-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 5.1
|
19
|
+
version: '5.1'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 5.1
|
26
|
+
version: '5.1'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: engine_cart
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -117,6 +117,8 @@ files:
|
|
117
117
|
- app/models/annotot/application_record.rb
|
118
118
|
- app/views/annotot/annotations/index.json.erb
|
119
119
|
- app/views/annotot/annotations/lists.json.jbuilder
|
120
|
+
- app/views/annotot/annotations/pages.json.jbuilder
|
121
|
+
- app/views/annotot/annotations/show.json.erb
|
120
122
|
- app/views/layouts/annotot/application.html.erb
|
121
123
|
- config/routes.rb
|
122
124
|
- db/migrate/20180215183225_create_annotot_annotations.rb
|
@@ -144,8 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
144
146
|
- !ruby/object:Gem::Version
|
145
147
|
version: '0'
|
146
148
|
requirements: []
|
147
|
-
|
148
|
-
rubygems_version: 2.6.11
|
149
|
+
rubygems_version: 3.1.2
|
149
150
|
signing_key:
|
150
151
|
specification_version: 4
|
151
152
|
summary: Annotot. Open annotations in Rails.
|