Annopolis

Annotation

Annotations returned by the API either by themselves or embedded in a parent object, should be in the following format.

The annotation server tries to keep output format close to the format of Mirador-created annotations. In the examples below, the “Canonical” tab shows the format from the current version of Mirador. The “old” format is still supported as of Mirador v2.6.

The main difference is the “on” field. In the new format, “on” is an array, and the selector is of type “oa:Choice” and contains both rectagular boundary and the SVG shape. The old format has “on” as a single object and selector doesn’t contain the rectangular boundary.

{
  "@id": "http://annotations.ten-thousand-rooms.yale.edu/annotations/e8a459bb-119c-4f46-87a3-93edc2cd22c1",
  "@type": "oa:Annotation",
  "@context": "http://iiif.io/api/presentation/2/context.json",
  "resource": [
    {
      "@type": "dctypes:Text",
      "format": "text/html",
      "chars": "<p>Test #1</p>"
    },
    {
      "@type": "oa:Tag",
      "chars": "mytag"
    }
  ],
  "within": [
    "http://annotations.ten-thousand-rooms.yale.edu/lists/http://manifest.tenthousandrooms.yale.edu/layers/16_http://manifest.tenthousandrooms.yale.edu/node/311/canvas/14116"
  ],
  "motivation": [
    "oa:commenting"
  ],
  "on": [
    {
      "@type": "oa:SpecificResource",
      "full": "http://manifest.tenthousandrooms.yale.edu/node/311/canvas/14116",
      "selector": {
        "@type": "oa:Choice",
        "default": {
          "@type": "oa:FragmentSelector",
          "value": "xywh=54,248,179,219"
        },
        "item": {
          "@type": "oa:SvgSelector",
          "value": "<svg xmlns='http://www.w3.org/2000/svg'><path xmlns=\"http://www.w3.org/2000/svg\" d=\"M53.93942,248.15395h89.65346v0h89.65346v109.58331v109.58331h-89.65346h-89.65346v-109.58331z\" data-paper-data=\"{&quot;strokeWidth&quot;:1,&quot;rotation&quot;:0,&quot;deleteIcon&quot;:null,&quot;rotationIcon&quot;:null,&quot;group&quot;:null,&quot;editable&quot;:true,&quot;annotation&quot;:null}\" id=\"rectangle_f95e7e02-7a2b-495e-8dc4-d47f1c0fba9d\" fill-opacity=\"0\" fill=\"#00bfff\" fill-rule=\"nonzero\" stroke=\"#00bfff\" stroke-width=\"1\" stroke-linecap=\"butt\" stroke-linejoin=\"miter\" stroke-miterlimit=\"10\" stroke-dasharray=\"\" stroke-dashoffset=\"0\" font-family=\"none\" font-weight=\"none\" font-size=\"none\" text-anchor=\"none\" style=\"mix-blend-mode: normal\"/></svg>"
        }
      },
      "within": {
        "@id": "http://manifest.tenthousandrooms.yale.edu/node/311/manifest",
        "@type": "sc:Manifest"
      }
    }
  ],
  "layerId": "http://manifest.tenthousandrooms.yale.edu/layers/16"
}
{
  "@id": "http://annotations.ten-thousand-rooms.yale.edu/annotations/215bc9b4-ca41-4467-a823-4440c420eb8e",
  "@type": "oa:annotation",
  "@context": "http://iiif.io/api/presentation/2/context.json",
  "resource": [
    {
      "@type": "dctypes:Text",
      "format": "text/html",
      "chars": "<p>《孔子集語》&nbsp;</p>"
    }
  ],
  "within": [
    "http://annotations.ten-thousand-rooms.yale.edu/lists/http://manifest.tenthousandrooms.yale.edu/layers/16_http://manifest.tenthousandrooms.yale.edu/node/311/canvas/14116",
    "http://annotations.tenkr.yale.edu/annotations/lists/http://ten-thousand-rooms.herokuapp.com/layers/5557c1c3-53d5-4af5-bfc8-990008826fcc_http://manifest.tenthousandrooms.yale.edu/node/311/canvas/14116"
  ],
  "motivation": [
    "oa:commenting"
  ],
  "on": {
    "@type": "oa:SpecificResource",
    "full": "http://manifest.tenthousandrooms.yale.edu/node/311/canvas/14116",
    "selector": {
      "@type": "oa:SvgSelector",
      "value": "<svg xmlns='http://www.w3.org/2000/svg'><path xmlns=\"http://www.w3.org/2000/svg\" d=\"M292.21935,122.21566l109.99409,0l109.99409,0l0,476.64106l0,476.64106l-109.99409,0l-109.99409,0l0,-476.64106z\" data-paper-data=\"{&quot;rotation&quot;:0,&quot;annotation&quot;:null}\" id=\"rectangle_647a69af-6014-412d-8c28-6e561ec82dde\" fill-opacity=\"0\" fill=\"#00bfff\" stroke=\"#00bfff\" stroke-width=\"1.74594\" stroke-linecap=\"butt\" stroke-linejoin=\"miter\" stroke-miterlimit=\"10\" stroke-dasharray=\"\" stroke-dashoffset=\"0\" font-family=\"sans-serif\" font-weight=\"normal\" font-size=\"12\" text-anchor=\"start\" mix-blend-mode=\"normal\"/></svg>"
    }
  },
  "layerId": "http://manifest.tenthousandrooms.yale.edu/layers/16"
}

Authentication

Authentication for create, update, and delete operations are handled through JWT tokens. Mirador is initialized with a token for the project and the user from the Drupal portal and passes it to annotation server along with its ajax requests. The annotation server decodes the token using a shared key and queries the portal to see if the user is allowed to perform the operation.

Dependencies

API dependencies

  • ${drupal_portal_url}/has-canvas-access?canvas_id=${canvas_id}&user_id=${user_id} - to check if the user ID (extracted from the JWT token) has permission for the canvas. In Drupal, this API is impemented as a view with a contextual filter.
  • ${drupal_portal_url}/node/#{project_id}/collection?user_id=#{user_id} - to get collection information for exporting annotations data for the project.

Imagemagick

  • The feed_for_search:bounding_boxes rake task depends on the rmagick gem, which in turn depends on the native installation of Imagemagick (note: version 6, not 7).

Environment

Environment Variables
IIIF_HOST_URL Entity IDs will be prefixed with this URL
USE_REDIS If Y, /getAnnotationsList is served from the Redis cache
S3_Bucket URL of the S3 bucket to which export and search feed files are uploaded
S3_Bucket_Folder Folder name under the said S3 bucket
S3_Key S3 credential
S3_Secret S3 credential
S3_PUBLIC_DOWNLOAD_PREFIX URL prefix from which to download exported CSV files
IIIF_COLLECTIONS_HOST URL of host that provides the collection information for exports
USE_JWT_AUTH If ‘Y’, authenticate the REST API using JWT tokens

For local development only:

DB_HOST_DEV
DATABASE_DEV
DB_USERNAME_DEV
DB_PASSWORD_DEV
DB_HOST_TEST
DATABASE_TEST
DB_USERNAME_TEST
DB_PASSWORD_TEST

Life of the Buddha

The features unique to the Life of the Buddha project have been omitted from this documentation.

API

  • /setRedisKeys - reload redis cache

Offline tasks

Various tasks that import annotations data from Google Docs.

/getAnnotationsViaList

Get annotations on the canvas

Parameters
canvas_id
ID of canvas
e.g. http://example.org/iiif/canvas/1
(required)

Returns all annotations (along with the IDs of the layers to which they belong) that are associated with the canvas transitively, i.e., recursively including annotations that target those annotations, thus indirectly targeting the canvas.

[
  {
    "layer_id" : "<Layer ID>",
    "annotation": "<See Documentation/Annotation section>"
  },
  ...
]
jQuery.ajax({
  url: "https://mirador-annotations-lotb-stg.herokuapp.com/getAnnotationsViaList?canvas_id=http%3A%2F%2Fmanifests.ydc2.yale.edu%2FLOTB%2Fcanvas%2Fpanel_01",
  type: 'GET',
  dataType: 'json',
  contentType: 'application/json; charset=utf-8',
  success: (data, textStatus, jqXHR) => {
    console.log('Success', data);
  },
  error: (jqXHR, textStatus, errorThrown) => {
    console.log('Error', jqXHR);
  }
});

/annotations/<id>

Get a specific annotation

Example: http://example.org/annotations/215bc9b4-ca41-4467-a823-4440c420eb8e

/annotations

Create an annotation

Payload:
 {
  "layer_id": "<Layer ID>",
  "annotation": "<See Documentation/Annotation section>"
}

annotation["@id"] is not required because it will be created by the server and included in response.

/annotations

Update annotation

Payload:
 {
  "layer_id": [ "<Layer ID>" ],
  "annotation": "<See Documentation/Annotation section>"
}

/annotations

Delete annotation

The whole URL of the request is identical to the IIIF style annotation ID, e.g., http://annotations.ten-thousand-rooms.yale.edu/annotations/76d24e36-e255-41db-883f-64606c4ff08e

/layers

Get annotation layers

Parameters
group_id
Group ID of current user.
When specified, returns list of layers that are defined for the group (or project).
(optional - currently used for Ten Thousand Rooms only.)
[
  {
    "@context": "http://iiif.io/api/presentation/2/context.json",
    "@id": "http://manifest.tenthousandrooms.yale.edu/layers/16",
    "@type": "sc:Layer",
    "label": "Transcription"
  },
  ...
]

/resequenceList

Re-order annotations in a list

Payload:
 {
  "canvas_id": "<Canvas ID>",
  "layer_id": "<Layer ID>",
  "annotation_ids": [ "<AnnotationID>", ... ]
}

canvas_id and layer_id together determines the list the user wants to change.

annotation_ids is a list of annotation IDs that are arranged in the new updated order.

/export

Export annotations

Parameters
user_id
User ID from drupal
(required)
project_id
Project ID from drupal
(required)

/export/check_status

Called by client-side JavaScript in intervals to update the status of the export job -- whether it is in progress or complete.

Parameters
job_id
ID of the Delayed::Job job

Used by the export page to check the progress of the offline export task. It should initiate download when the status indicates the job is complete.

/setCurrentLayers

Set layers (called from drupal)

Payload:
 {
  "group_id": "<Group ID>",
  "group_description": "<Node title>",
  "layers": [
    {
      "layer_id": "<Layer ID>",
      "label": "<label>"
    },
    ...
  ]
}

Feed for search

Exports annotations data into csv files and uploads them to S3 so the drupal portal will consume them to feed the Solr search engine.

Rake tasks

  • feed_for_search:bounding_boxes
  • feed_for_search:annos_no_resource
  • feed_for_search:annos_resource_only