activeadmin_annotations 0.1.1 → 0.1.2
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/CHANGELOG.md +12 -0
- data/CODE_OF_CONDUCT.md +14 -19
- data/CONTRIBUTING.md +9 -15
- data/GOVERNANCE.md +10 -14
- data/README.md +24 -7
- data/SECURITY.md +14 -16
- data/activeadmin_annotations.gemspec +2 -2
- data/admin/annotation_reviews.rb +14 -6
- data/admin/annotations.rb +33 -13
- data/app/assets/controllers/activeadmin_annotations/annotation_client.js +44 -0
- data/app/assets/controllers/activeadmin_annotations/annotation_list.js +29 -0
- data/app/assets/controllers/activeadmin_annotations/annotator_controller.js +45 -370
- data/app/assets/controllers/activeadmin_annotations/composer.js +179 -0
- data/app/assets/controllers/activeadmin_annotations/highlights.js +76 -0
- data/app/assets/controllers/activeadmin_annotations/selection.js +73 -0
- data/app/assets/javascripts/activeadmin_annotations/annotator_logic.mjs +73 -0
- data/app/assets/stylesheets/activeadmin_annotations.css +15 -0
- data/app/helpers/active_admin/annotations/panel_helper.rb +15 -8
- data/app/models/active_admin/annotations/review.rb +7 -2
- data/app/services/active_admin/annotations/review_service.rb +34 -27
- data/app/views/active_admin/annotations/_panel.html.erb +15 -2
- data/db/migrate/20260617120000_create_activeadmin_annotations_tables.rb +27 -10
- data/db/migrate/20260706150000_add_metadata_json_gin_index_to_annotation_reviews.rb +1 -1
- data/db/migrate/20260710120000_add_panel_query_index_to_annotation_spans.rb +1 -1
- data/lib/activeadmin/annotations/copy_text.rb +0 -1
- data/lib/activeadmin/annotations/engine.rb +16 -1
- data/lib/activeadmin/annotations/exporter.rb +1 -1
- data/lib/activeadmin/annotations/panel_html.rb +21 -0
- data/lib/activeadmin/annotations/review_access.rb +13 -1
- data/lib/activeadmin/annotations/review_panel.rb +9 -6
- data/lib/activeadmin/annotations/span_create.rb +70 -0
- data/lib/activeadmin/annotations/version.rb +1 -1
- data/lib/activeadmin_annotations.rb +2 -0
- metadata +12 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 552186e90302ae8e259a730bed26b0c7f8de35070f98d60accfcd81f17c95ba8
|
|
4
|
+
data.tar.gz: '048dfedf289691780333a9ab149475f106d2ddc3832cf90d3f6fd153d7192038'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c8fb4cfa2c652a145ce5ec4e263b9c1d37e2d4f9899cee3c72eab271e18245dbb9780f2f29c595a3fc8be26b6900731d1f6b0d9468860ef3d2a12ae2620559a2
|
|
7
|
+
data.tar.gz: 58c44f9168cbd4e19dd346891a917e408aab9758b98889cbeaa12e1cf40bdaa7053fac0a69b1fcc9f71f02175a7f00fd9513814c6c43ffa65626a3fff1d0f9d3
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## 0.1.2 (2026-09-04)
|
|
4
|
+
|
|
5
|
+
- Create the review record when the first annotation is saved.
|
|
6
|
+
- Keep the annotator read-only when browsing a content version other than the one under review.
|
|
7
|
+
- Show This version is read-only. when a save targets a different content version.
|
|
8
|
+
- Show Annotation access denied when a reviewer opens or changes another reviewer's annotation.
|
|
9
|
+
- Limit the review form to status and notes.
|
|
10
|
+
- Omit reviewer identity from copied annotation text.
|
|
11
|
+
- Install annotation tables on SQLite with json columns and on PostgreSQL with jsonb.
|
|
12
|
+
- Escape ordinary panel strings and sanitize html-safe host HTML.
|
|
13
|
+
- Show saved and pending marks when the browser has no custom highlight support.
|
|
14
|
+
|
|
3
15
|
## 0.1.1 (2026-07-14)
|
|
4
16
|
|
|
5
17
|
- Return forbidden responses with access-denied messages for cross-reviewer review and annotation mutations instead of not-found wording.
|
data/CODE_OF_CONDUCT.md
CHANGED
|
@@ -1,31 +1,26 @@
|
|
|
1
1
|
# Code of Conduct
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Pledge
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Participation is a harassment-free experience. Treat each other with respect. Keep private context private.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Expected behavior
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
- Engage with genuine curiosity. Admit uncertainty.
|
|
10
|
+
- Own every line you submit, including generated code.
|
|
11
|
+
- Correct errors politely.
|
|
12
|
+
- Use language that welcomes people and respects identity and privacy.
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
- Responsible innovation: taking full responsibility for any content or code contributed, whether manually written or generated by automation tools.
|
|
13
|
-
- Gentle correction: responding politely to errors. We view mistakes as opportunities for learning, provided they are addressed with humility.
|
|
14
|
-
- Inclusive language: using language that welcomes diverse perspectives and respects the privacy and identity of all participants.
|
|
14
|
+
## Unacceptable behavior
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
- Harassment, trolling, or insulting comments, public or private.
|
|
17
|
+
- Using jargon or volume, including automated spam, to silence others.
|
|
18
|
+
- Sharing others' data or personal context without permission.
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
- Weaponized complexity: using jargon or overwhelming volume (including automated spam) to silence others.
|
|
20
|
-
- Publishing private information: sharing others' data or personal context without explicit permission.
|
|
20
|
+
## Automation
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
In accordance with our commitment to collective awareness:
|
|
25
|
-
|
|
26
|
-
- Contributors are responsible for the accuracy and security of any AI-generated artifacts they submit.
|
|
27
|
-
- "The AI wrote it" is not a valid excuse for introducing bugs, security vulnerabilities, or bias.
|
|
22
|
+
You remain responsible for bugs, security holes, and bias in generated artifacts you submit.
|
|
28
23
|
|
|
29
24
|
## Enforcement
|
|
30
25
|
|
|
31
|
-
|
|
26
|
+
Report abusive or harassing behavior to contact@kiskolabs.com. Reports are reviewed and investigated.
|
data/CONTRIBUTING.md
CHANGED
|
@@ -1,29 +1,23 @@
|
|
|
1
1
|
# Contributing Guidelines
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## Automation authorship
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
You may use AI and automation tools. You remain the author of every line you submit: review, debug, and understand the change.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Keep pull requests small enough to review. Annotate non-obvious logic.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
2. Manage cognitive load. Do not submit massive, unreviewed automated dumps. Respect the reviewers' time by annotating complex logic.
|
|
11
|
-
3. Security. Never feed project secrets or private context into public AI models.
|
|
9
|
+
Never send project secrets or private context to public models.
|
|
12
10
|
|
|
13
11
|
## How to contribute
|
|
14
12
|
|
|
15
13
|
### Reporting issues
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
- Use issue templates to provide clear goals, constraints, and reproduction steps.
|
|
15
|
+
Verify the facts before filing. Use issue templates for goals, constraints, and reproduction steps.
|
|
19
16
|
|
|
20
|
-
### Pull
|
|
17
|
+
### Pull requests
|
|
21
18
|
|
|
22
|
-
|
|
23
|
-
- Context: explain why the change is necessary.
|
|
24
|
-
- Testing: run `make test` locally before opening a PR.
|
|
19
|
+
Keep one pull request to one goal. Explain why the change is needed. Run smoke tests and regression checks locally before opening the request.
|
|
25
20
|
|
|
26
|
-
### Review
|
|
21
|
+
### Review
|
|
27
22
|
|
|
28
|
-
|
|
29
|
-
- If a reviewer suggests a change, view it as mutual aid, not criticism.
|
|
23
|
+
Reviewers will ask about approach and trade-offs. Treat suggested changes as part of the work.
|
data/GOVERNANCE.md
CHANGED
|
@@ -1,29 +1,25 @@
|
|
|
1
1
|
# Project Governance
|
|
2
2
|
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
This project balances individual autonomy with collective coordination. We aim for a workflow that supports rapid iteration while maintaining strict guardrails for safety and architectural integrity.
|
|
6
|
-
|
|
7
3
|
## Roles
|
|
8
4
|
|
|
9
5
|
### Contributors
|
|
10
6
|
|
|
11
|
-
Anyone who submits code, documentation, or participates in discussions. Contributors
|
|
7
|
+
Anyone who submits code, documentation, or participates in discussions. Contributors take ownership of problems they identify and follow the project's quality standards.
|
|
12
8
|
|
|
13
|
-
### Maintainers
|
|
9
|
+
### Maintainers
|
|
14
10
|
|
|
15
|
-
Maintainers
|
|
11
|
+
Maintainers:
|
|
16
12
|
|
|
17
|
-
1.
|
|
18
|
-
2. Review
|
|
19
|
-
3.
|
|
13
|
+
1. Set scope and architectural direction.
|
|
14
|
+
2. Review contributions (human or automated) against security and logic standards.
|
|
15
|
+
3. Decide when the community is divided.
|
|
20
16
|
|
|
21
|
-
## Decision making
|
|
17
|
+
## Decision making
|
|
22
18
|
|
|
23
19
|
### Lazy consensus
|
|
24
20
|
|
|
25
|
-
For
|
|
21
|
+
For routine changes, a proposal that draws no objection within two weeks is approved.
|
|
26
22
|
|
|
27
|
-
###
|
|
23
|
+
### Maintainer approval
|
|
28
24
|
|
|
29
|
-
Major architectural changes, high-risk automation integrations, or changes that affect business logic
|
|
25
|
+
Major architectural changes, high-risk automation integrations, or changes that affect business logic need explicit maintainer approval. A change must solve a real problem.
|
data/README.md
CHANGED
|
@@ -8,10 +8,10 @@ The gem stays domain-agnostic: the host app chooses what review context means, w
|
|
|
8
8
|
|
|
9
9
|
## Requirements
|
|
10
10
|
|
|
11
|
-
- Ruby 3.
|
|
11
|
+
- Ruby 3.4+
|
|
12
12
|
- Rails 7.1+
|
|
13
13
|
- ActiveAdmin 3.0+
|
|
14
|
-
-
|
|
14
|
+
- SQLite (`json` columns) or PostgreSQL (`jsonb`, plus GIN on review metadata). CI appraisals start at Rails 7.2.
|
|
15
15
|
|
|
16
16
|
## Host app setup
|
|
17
17
|
|
|
@@ -89,7 +89,9 @@ text_node helpers.activeadmin_annotations_panel(
|
|
|
89
89
|
)
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
Ordinary strings are escaped. html-safe host HTML is sanitized so paragraphs stay and scripts and event handlers are stripped.
|
|
93
|
+
|
|
94
|
+
The gem stores annotations against the live subject text. The review row is created on the first annotation save, not when the panel is shown. If the reviewed text changes, stale detection uses `context_digest` only. No version UI is shown.
|
|
93
95
|
|
|
94
96
|
## Content revisions (optional)
|
|
95
97
|
|
|
@@ -127,7 +129,7 @@ class Article < ApplicationRecord
|
|
|
127
129
|
end
|
|
128
130
|
```
|
|
129
131
|
|
|
130
|
-
Then either render the panel through `ActiveAdmin::Annotations::ReviewPanel` (handles pinning, stale state, and version filtering):
|
|
132
|
+
Then either render the panel through `ActiveAdmin::Annotations::ReviewPanel` (handles pinning, stale state, and version filtering). Browsing a content version other than the one under review is read-only. Add notes on the version being reviewed:
|
|
131
133
|
|
|
132
134
|
```ruby
|
|
133
135
|
text_node ActiveAdmin::Annotations::ReviewPanel.render(
|
|
@@ -231,11 +233,13 @@ Add a host stylesheet that loads after the gem asset (for example in your Active
|
|
|
231
233
|
border-color: var(--primary, #2563eb);
|
|
232
234
|
}
|
|
233
235
|
|
|
234
|
-
::highlight(aa-annotations-saved)
|
|
236
|
+
::highlight(aa-annotations-saved),
|
|
237
|
+
.aa-annotations-mark--saved {
|
|
235
238
|
background-color: color-mix(in srgb, var(--primary, #2563eb) 30%, #fef08a);
|
|
236
239
|
}
|
|
237
240
|
|
|
238
|
-
::highlight(aa-annotations-pending)
|
|
241
|
+
::highlight(aa-annotations-pending),
|
|
242
|
+
.aa-annotations-mark--pending {
|
|
239
243
|
background-color: color-mix(in srgb, var(--primary, #2563eb) 40%, #fef08a);
|
|
240
244
|
text-decoration-color: var(--primary, #2563eb);
|
|
241
245
|
}
|
|
@@ -298,7 +302,7 @@ bundle exec rubocop
|
|
|
298
302
|
bundle exec polyrun parallel-rspec --workers 5 --merge-failures
|
|
299
303
|
```
|
|
300
304
|
|
|
301
|
-
Matrixed Rails versions use [Appraisal](https://github.com/thoughtbot/appraisal): `gemfiles/rails72.gemfile
|
|
305
|
+
Matrixed Rails versions use [Appraisal](https://github.com/thoughtbot/appraisal): `gemfiles/rails72.gemfile` and `rails8ruby34.gemfile`. Run `bundle exec appraisal rspec` to execute RSpec in each gemfile context.
|
|
302
306
|
|
|
303
307
|
Shared agent guidance is managed with [pray](https://github.com/kiskolabs/pray) via `Prayfile`.
|
|
304
308
|
|
|
@@ -308,6 +312,19 @@ Shared agent guidance is managed with [pray](https://github.com/kiskolabs/pray)
|
|
|
308
312
|
|
|
309
313
|
See [CONTRIBUTING.md](CONTRIBUTING.md). Bug reports and pull requests are welcome at https://github.com/amkisko/activeadmin_annotations.rb/issues
|
|
310
314
|
|
|
315
|
+
## Links
|
|
316
|
+
|
|
317
|
+
- [GitHub](https://github.com/amkisko/activeadmin_annotations.rb)
|
|
318
|
+
- [GitLab](https://gitlab.com/amkisko/activeadmin_annotations.rb)
|
|
319
|
+
- [RubyGems](https://rubygems.org/gems/activeadmin_annotations)
|
|
320
|
+
- [Versions Atom](https://rubygems.org/gems/activeadmin_annotations/versions.atom) (feed id `11581035749940`)
|
|
321
|
+
- [libraries.io](https://libraries.io/rubygems/activeadmin_annotations)
|
|
322
|
+
- [Deps.dev](https://deps.dev/rubygems/activeadmin_annotations)
|
|
323
|
+
- [SonarCloud](https://sonarcloud.io/project/overview?id=amkisko_activeadmin_annotations.rb)
|
|
324
|
+
- [Snyk](https://snyk.io/test/github/amkisko/activeadmin_annotations.rb)
|
|
325
|
+
- [Codecov](https://app.codecov.io/github/amkisko/activeadmin_annotations.rb)
|
|
326
|
+
- [OpenSSF Scorecard](https://scorecard.dev/viewer/?uri=github.com/amkisko/activeadmin_annotations.rb)
|
|
327
|
+
|
|
311
328
|
## License
|
|
312
329
|
|
|
313
330
|
MIT — see [LICENSE.md](LICENSE.md).
|
data/SECURITY.md
CHANGED
|
@@ -1,27 +1,25 @@
|
|
|
1
1
|
# SECURITY
|
|
2
2
|
|
|
3
|
-
## Reporting a
|
|
4
|
-
|
|
5
|
-
Do NOT open a public GitHub issue for security vulnerabilities.
|
|
3
|
+
## Reporting a Vulnerability
|
|
6
4
|
|
|
7
5
|
Email security details to: security@kiskolabs.com
|
|
8
6
|
|
|
9
|
-
Include
|
|
7
|
+
Send the report only to that address. Include description, steps to reproduce, potential impact, and a suggested fix when you have one.
|
|
10
8
|
|
|
11
|
-
### Response
|
|
9
|
+
### Response Timeline
|
|
12
10
|
|
|
13
|
-
- We will acknowledge receipt of your report
|
|
14
|
-
- We will provide an initial assessment
|
|
15
|
-
- We will keep you informed of our progress and resolution timeline
|
|
11
|
+
- We will acknowledge receipt of your report.
|
|
12
|
+
- We will provide an initial assessment.
|
|
13
|
+
- We will keep you informed of our progress and resolution timeline.
|
|
16
14
|
|
|
17
|
-
### Disclosure
|
|
15
|
+
### Disclosure Policy
|
|
18
16
|
|
|
19
|
-
- We will work with you to understand and resolve the issue
|
|
20
|
-
- We will credit you for the discovery
|
|
21
|
-
- We will publish a security advisory after the vulnerability is patched
|
|
22
|
-
- We will coordinate public disclosure with you
|
|
17
|
+
- We will work with you to understand and resolve the issue.
|
|
18
|
+
- We will credit you for the discovery unless you prefer to remain anonymous.
|
|
19
|
+
- We will publish a security advisory after the vulnerability is patched.
|
|
20
|
+
- We will coordinate public disclosure with you.
|
|
23
21
|
|
|
24
|
-
## Automation
|
|
22
|
+
## Automation Security
|
|
25
23
|
|
|
26
|
-
-
|
|
27
|
-
-
|
|
24
|
+
- Keep production credentials, API keys, and personal data out of prompts sent to third-party models or automation services.
|
|
25
|
+
- Verify automated dependencies.
|
|
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
|
|
|
12
12
|
spec.description = "Collect text highlights and comments inside a bounded ActiveAdmin block, with optional review context, content revision pinning, and JSONL export."
|
|
13
13
|
spec.license = "MIT"
|
|
14
14
|
spec.platform = Gem::Platform::RUBY
|
|
15
|
-
spec.required_ruby_version = ">= 3.
|
|
15
|
+
spec.required_ruby_version = ">= 3.4"
|
|
16
16
|
|
|
17
17
|
repository_url = "https://github.com/amkisko/activeadmin_annotations.rb"
|
|
18
18
|
|
|
@@ -64,5 +64,5 @@ Gem::Specification.new do |spec|
|
|
|
64
64
|
spec.add_development_dependency "standard-performance", "~> 1.8"
|
|
65
65
|
spec.add_development_dependency "standard-rails", "~> 1.5"
|
|
66
66
|
spec.add_development_dependency "standard-rspec", "~> 0.3"
|
|
67
|
-
spec.add_development_dependency "rbs", "~>
|
|
67
|
+
spec.add_development_dependency "rbs", "~> 4"
|
|
68
68
|
end
|
data/admin/annotation_reviews.rb
CHANGED
|
@@ -8,13 +8,16 @@ ActiveAdmin.register ActiveAdmin::Annotations::Review, as: "annotation_reviews"
|
|
|
8
8
|
|
|
9
9
|
controller do
|
|
10
10
|
def scoped_collection
|
|
11
|
-
|
|
11
|
+
annotation_counts = ActiveAdmin::Annotations::Annotation
|
|
12
|
+
.where("annotation_spans.annotation_review_id = annotation_reviews.id")
|
|
13
|
+
.select("COUNT(*)")
|
|
14
|
+
super.select("annotation_reviews.*", "(#{annotation_counts.to_sql}) AS annotations_count")
|
|
12
15
|
end
|
|
13
16
|
end
|
|
14
17
|
|
|
15
18
|
actions :index, :show, :create, :update
|
|
16
19
|
|
|
17
|
-
permit_params :review_status, :notes
|
|
20
|
+
permit_params :review_status, :notes
|
|
18
21
|
|
|
19
22
|
filter :subject_type, as: :select
|
|
20
23
|
filter :subject_id
|
|
@@ -36,7 +39,7 @@ ActiveAdmin.register ActiveAdmin::Annotations::Review, as: "annotation_reviews"
|
|
|
36
39
|
column :subject_id
|
|
37
40
|
column :reviewer
|
|
38
41
|
column :review_status
|
|
39
|
-
column("Annotations") { |review| review.
|
|
42
|
+
column("Annotations") { |review| review.annotations_count }
|
|
40
43
|
column :context_digest
|
|
41
44
|
column :created_at
|
|
42
45
|
actions
|
|
@@ -91,9 +94,14 @@ ActiveAdmin.register ActiveAdmin::Annotations::Review, as: "annotation_reviews"
|
|
|
91
94
|
|
|
92
95
|
collection_action :export_jsonl, method: :get do
|
|
93
96
|
exporter = ActiveAdmin::Annotations::Exporter.new(reviews: collection)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
+
filename = "annotation-reviews-#{Time.current.strftime("%Y%m%d%H%M%S")}.jsonl"
|
|
98
|
+
headers["Content-Type"] = "application/jsonl"
|
|
99
|
+
headers["Content-Disposition"] = %(attachment; filename="#{filename}")
|
|
100
|
+
self.response_body = Enumerator.new do |yielder|
|
|
101
|
+
exporter.each_jsonl_row do |row|
|
|
102
|
+
yielder << "#{JSON.generate(row)}\n"
|
|
103
|
+
end
|
|
104
|
+
end
|
|
97
105
|
end
|
|
98
106
|
|
|
99
107
|
action_item :export_jsonl, only: :index do
|
data/admin/annotations.rb
CHANGED
|
@@ -59,13 +59,22 @@ ActiveAdmin.register ActiveAdmin::Annotations::Annotation, as: "annotation_spans
|
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
controller do
|
|
62
|
-
before_action :authorize_annotation_access!, only: %i[update destroy copy_text]
|
|
62
|
+
before_action :authorize_annotation_access!, only: %i[show update destroy copy_text]
|
|
63
|
+
|
|
64
|
+
def scoped_collection
|
|
65
|
+
return super unless action_name == "index"
|
|
66
|
+
|
|
67
|
+
reviewer = current_annotation_reviewer
|
|
68
|
+
return super.none if reviewer.blank?
|
|
69
|
+
|
|
70
|
+
super.joins(:annotation_review).where(annotation_reviews: {reviewer_id: reviewer.id})
|
|
71
|
+
end
|
|
63
72
|
|
|
64
73
|
def create
|
|
65
74
|
review = authorize_review_for_create!
|
|
66
75
|
return if performed?
|
|
67
76
|
|
|
68
|
-
|
|
77
|
+
stamp_span_from_review!(review)
|
|
69
78
|
super do |success, failure|
|
|
70
79
|
success.html { redirect_to request.referer || resource_path, notice: "Annotation saved." }
|
|
71
80
|
success.json { render json: annotation_json(resource), status: :created }
|
|
@@ -95,12 +104,18 @@ ActiveAdmin.register ActiveAdmin::Annotations::Annotation, as: "annotation_spans
|
|
|
95
104
|
private
|
|
96
105
|
|
|
97
106
|
def authorize_review_for_create!
|
|
98
|
-
ActiveAdmin::Annotations::
|
|
99
|
-
|
|
107
|
+
ActiveAdmin::Annotations::SpanCreate.review_for!(
|
|
108
|
+
params: params,
|
|
100
109
|
reviewer: current_annotation_reviewer
|
|
101
110
|
)
|
|
102
|
-
rescue ActiveAdmin::Annotations::
|
|
103
|
-
|
|
111
|
+
rescue ActiveAdmin::Annotations::SpanCreate::ReadOnly => error
|
|
112
|
+
respond_to_span_error(error, status: :unprocessable_entity)
|
|
113
|
+
nil
|
|
114
|
+
rescue ActiveAdmin::Annotations::ReviewAccess::NotFound => error
|
|
115
|
+
respond_to_span_error(error, status: :not_found)
|
|
116
|
+
nil
|
|
117
|
+
rescue ActiveAdmin::Annotations::ReviewAccess::Forbidden => error
|
|
118
|
+
respond_to_span_error(error, status: :forbidden)
|
|
104
119
|
nil
|
|
105
120
|
end
|
|
106
121
|
|
|
@@ -109,8 +124,10 @@ ActiveAdmin.register ActiveAdmin::Annotations::Annotation, as: "annotation_spans
|
|
|
109
124
|
annotation: resource,
|
|
110
125
|
reviewer: current_annotation_reviewer
|
|
111
126
|
)
|
|
112
|
-
rescue ActiveAdmin::Annotations::ReviewAccess::
|
|
113
|
-
|
|
127
|
+
rescue ActiveAdmin::Annotations::ReviewAccess::NotFound => error
|
|
128
|
+
respond_to_span_error(error, status: :not_found)
|
|
129
|
+
rescue ActiveAdmin::Annotations::ReviewAccess::Forbidden => error
|
|
130
|
+
respond_to_span_error(error, status: :forbidden)
|
|
114
131
|
end
|
|
115
132
|
|
|
116
133
|
def current_annotation_reviewer
|
|
@@ -119,21 +136,24 @@ ActiveAdmin.register ActiveAdmin::Annotations::Annotation, as: "annotation_spans
|
|
|
119
136
|
nil
|
|
120
137
|
end
|
|
121
138
|
|
|
122
|
-
def
|
|
139
|
+
def respond_to_span_error(error, status:)
|
|
123
140
|
if request.format.json?
|
|
124
|
-
render json: {errors: [
|
|
141
|
+
render json: {errors: [error.message]}, status: status
|
|
125
142
|
else
|
|
126
|
-
redirect_to request.referer || admin_root_path, alert:
|
|
143
|
+
redirect_to request.referer || admin_root_path, alert: error.message
|
|
127
144
|
end
|
|
128
145
|
end
|
|
129
146
|
|
|
130
|
-
def
|
|
131
|
-
params[:annotation]
|
|
147
|
+
def stamp_span_from_review!(review)
|
|
148
|
+
annotation_params = params[:annotation] ||= ActionController::Parameters.new
|
|
149
|
+
annotation_params[:annotation_review_id] = review.id
|
|
150
|
+
annotation_params[:content_revision_version] = review.content_revision_version
|
|
132
151
|
end
|
|
133
152
|
|
|
134
153
|
def annotation_json(annotation)
|
|
135
154
|
{
|
|
136
155
|
id: annotation.id,
|
|
156
|
+
annotation_review_id: annotation.annotation_review_id,
|
|
137
157
|
field_name: annotation.field_name,
|
|
138
158
|
selected_text: annotation.selected_text,
|
|
139
159
|
start_offset: annotation.start_offset,
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export class AnnotationClient {
|
|
2
|
+
constructor({ csrfToken }) {
|
|
3
|
+
this.csrfToken = csrfToken;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
async create(url, payload) {
|
|
7
|
+
return this.requestJson(url, { method: "POST", body: payload });
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async update(url, payload) {
|
|
11
|
+
return this.requestJson(url, { method: "PATCH", body: payload });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async destroy(url) {
|
|
15
|
+
await this.requestJson(url, { method: "DELETE" });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async requestJson(url, { method, body }) {
|
|
19
|
+
const response = await fetch(url, {
|
|
20
|
+
method,
|
|
21
|
+
credentials: "same-origin",
|
|
22
|
+
headers: {
|
|
23
|
+
Accept: "application/json",
|
|
24
|
+
"X-CSRF-Token": this.csrfToken,
|
|
25
|
+
...(body ? { "Content-Type": "application/json" } : {}),
|
|
26
|
+
},
|
|
27
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
const error = new Error("request failed");
|
|
32
|
+
error.response = response;
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (method === "DELETE") return null;
|
|
37
|
+
return response.json();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function csrfToken() {
|
|
42
|
+
const meta = document.querySelector("meta[name='csrf-token']");
|
|
43
|
+
return meta ? meta.getAttribute("content") : "";
|
|
44
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { categoryLabelFor, escapeHtml } from "activeadmin_annotations/annotator_logic";
|
|
2
|
+
|
|
3
|
+
export function emptyAnnotationListItem() {
|
|
4
|
+
const item = document.createElement("p");
|
|
5
|
+
item.className = "aa-annotations-empty";
|
|
6
|
+
item.textContent = "No annotations yet.";
|
|
7
|
+
return item;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function annotationListItem(annotation, { readonly, categoryLabel, categories }) {
|
|
11
|
+
const item = document.createElement("article");
|
|
12
|
+
item.className = "aa-annotations-annotation-item";
|
|
13
|
+
const resolvedCategory = categoryLabelFor(annotation.category, categories);
|
|
14
|
+
const actions = readonly
|
|
15
|
+
? ""
|
|
16
|
+
: `
|
|
17
|
+
<div class="aa-annotations-annotation-actions">
|
|
18
|
+
<button type="button" class="action-item-button" data-annotation-id="${escapeHtml(String(annotation.id))}" data-action="activeadmin-annotations--annotator#editAnnotation">Edit</button>
|
|
19
|
+
<button type="button" class="action-item-button aa-annotations-delete-button" data-annotation-id="${escapeHtml(String(annotation.id))}" data-action="activeadmin-annotations--annotator#deleteAnnotation">Delete</button>
|
|
20
|
+
</div>
|
|
21
|
+
`;
|
|
22
|
+
item.innerHTML = `
|
|
23
|
+
<blockquote>${escapeHtml(annotation.selected_text)}</blockquote>
|
|
24
|
+
<p>${escapeHtml(annotation.comment)}</p>
|
|
25
|
+
${annotation.category ? `<p><strong>${escapeHtml(categoryLabel)}:</strong> ${escapeHtml(resolvedCategory)}</p>` : ""}
|
|
26
|
+
${actions}
|
|
27
|
+
`;
|
|
28
|
+
return item;
|
|
29
|
+
}
|