Skip to content

Commit

Permalink
Merge annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexanderWatzinger committed Dec 22, 2023
2 parents 9778856 + df86da7 commit 62ee720
Show file tree
Hide file tree
Showing 15 changed files with 550 additions and 36 deletions.
Empty file.
88 changes: 86 additions & 2 deletions install/1_structure.sql
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
-- PostgreSQL database dump
--

-- Dumped from database version 13.9 (Debian 13.9-0+deb11u1)
-- Dumped by pg_dump version 13.9 (Debian 13.9-0+deb11u1)
-- Dumped from database version 13.13 (Debian 13.13-0+deb11u1)
-- Dumped by pg_dump version 13.13 (Debian 13.13-0+deb11u1)

SET statement_timeout = 0;
SET lock_timeout = 0;
Expand Down Expand Up @@ -33,6 +33,9 @@ ALTER TABLE IF EXISTS ONLY web.hierarchy DROP CONSTRAINT IF EXISTS hierarchy_id_
ALTER TABLE IF EXISTS ONLY web.hierarchy_openatlas_class DROP CONSTRAINT IF EXISTS hierarchy_form_hierarchy_id_fkey;
ALTER TABLE IF EXISTS ONLY web.entity_profile_image DROP CONSTRAINT IF EXISTS entity_profile_image_image_id_fkey;
ALTER TABLE IF EXISTS ONLY web.entity_profile_image DROP CONSTRAINT IF EXISTS entity_profile_image_entity_id_fkey;
ALTER TABLE IF EXISTS ONLY web.annotation_image DROP CONSTRAINT IF EXISTS annotation_image_user_id_fkey;
ALTER TABLE IF EXISTS ONLY web.annotation_image DROP CONSTRAINT IF EXISTS annotation_image_image_id_fkey;
ALTER TABLE IF EXISTS ONLY web.annotation_image DROP CONSTRAINT IF EXISTS annotation_image_entity_id_fkey;
ALTER TABLE IF EXISTS ONLY model.property DROP CONSTRAINT IF EXISTS property_range_class_code_fkey;
ALTER TABLE IF EXISTS ONLY model.property_inheritance DROP CONSTRAINT IF EXISTS property_inheritance_super_code_fkey;
ALTER TABLE IF EXISTS ONLY model.property_inheritance DROP CONSTRAINT IF EXISTS property_inheritance_sub_code_fkey;
Expand Down Expand Up @@ -98,6 +101,7 @@ ALTER TABLE IF EXISTS ONLY web."group" DROP CONSTRAINT IF EXISTS group_pkey;
ALTER TABLE IF EXISTS ONLY web."group" DROP CONSTRAINT IF EXISTS group_name_key;
ALTER TABLE IF EXISTS ONLY web.entity_profile_image DROP CONSTRAINT IF EXISTS entity_profile_image_pkey;
ALTER TABLE IF EXISTS ONLY web.entity_profile_image DROP CONSTRAINT IF EXISTS entity_profile_image_entity_id_key;
ALTER TABLE IF EXISTS ONLY web.annotation_image DROP CONSTRAINT IF EXISTS annotation_image_pkey;
ALTER TABLE IF EXISTS ONLY model.property DROP CONSTRAINT IF EXISTS property_pkey;
ALTER TABLE IF EXISTS ONLY model.property_inheritance DROP CONSTRAINT IF EXISTS property_inheritance_pkey;
ALTER TABLE IF EXISTS ONLY model.property_i18n DROP CONSTRAINT IF EXISTS property_i18n_property_code_language_code_key;
Expand Down Expand Up @@ -133,6 +137,7 @@ ALTER TABLE IF EXISTS web.hierarchy_openatlas_class ALTER COLUMN id DROP DEFAULT
ALTER TABLE IF EXISTS web.hierarchy ALTER COLUMN id DROP DEFAULT;
ALTER TABLE IF EXISTS web."group" ALTER COLUMN id DROP DEFAULT;
ALTER TABLE IF EXISTS web.entity_profile_image ALTER COLUMN id DROP DEFAULT;
ALTER TABLE IF EXISTS web.annotation_image ALTER COLUMN id DROP DEFAULT;
ALTER TABLE IF EXISTS model.property_inheritance ALTER COLUMN id DROP DEFAULT;
ALTER TABLE IF EXISTS model.property_i18n ALTER COLUMN id DROP DEFAULT;
ALTER TABLE IF EXISTS model.property ALTER COLUMN id DROP DEFAULT;
Expand Down Expand Up @@ -174,6 +179,8 @@ DROP SEQUENCE IF EXISTS web.group_id_seq;
DROP TABLE IF EXISTS web."group";
DROP SEQUENCE IF EXISTS web.entity_profile_image_id_seq;
DROP TABLE IF EXISTS web.entity_profile_image;
DROP SEQUENCE IF EXISTS web.annotation_image_id_seq;
DROP TABLE IF EXISTS web.annotation_image;
DROP SEQUENCE IF EXISTS model.property_inheritance_id_seq;
DROP TABLE IF EXISTS model.property_inheritance;
DROP SEQUENCE IF EXISTS model.property_id_seq;
Expand Down Expand Up @@ -800,6 +807,44 @@ ALTER TABLE model.property_inheritance_id_seq OWNER TO openatlas;
ALTER SEQUENCE model.property_inheritance_id_seq OWNED BY model.property_inheritance.id;


--
-- Name: annotation_image; Type: TABLE; Schema: web; Owner: openatlas
--

CREATE TABLE web.annotation_image (
id integer NOT NULL,
image_id integer NOT NULL,
entity_id integer,
coordinates text NOT NULL,
user_id integer,
annotation text NOT NULL,
created timestamp without time zone DEFAULT now() NOT NULL
);


ALTER TABLE web.annotation_image OWNER TO openatlas;

--
-- Name: annotation_image_id_seq; Type: SEQUENCE; Schema: web; Owner: openatlas
--

CREATE SEQUENCE web.annotation_image_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;


ALTER TABLE web.annotation_image_id_seq OWNER TO openatlas;

--
-- Name: annotation_image_id_seq; Type: SEQUENCE OWNED BY; Schema: web; Owner: openatlas
--

ALTER SEQUENCE web.annotation_image_id_seq OWNED BY web.annotation_image.id;


--
-- Name: entity_profile_image; Type: TABLE; Schema: web; Owner: openatlas
--
Expand Down Expand Up @@ -1450,6 +1495,13 @@ ALTER TABLE ONLY model.property_i18n ALTER COLUMN id SET DEFAULT nextval('model.
ALTER TABLE ONLY model.property_inheritance ALTER COLUMN id SET DEFAULT nextval('model.property_inheritance_id_seq'::regclass);


--
-- Name: annotation_image id; Type: DEFAULT; Schema: web; Owner: openatlas
--

ALTER TABLE ONLY web.annotation_image ALTER COLUMN id SET DEFAULT nextval('web.annotation_image_id_seq'::regclass);


--
-- Name: entity_profile_image id; Type: DEFAULT; Schema: web; Owner: openatlas
--
Expand Down Expand Up @@ -1716,6 +1768,14 @@ ALTER TABLE ONLY model.property
ADD CONSTRAINT property_pkey PRIMARY KEY (id);


--
-- Name: annotation_image annotation_image_pkey; Type: CONSTRAINT; Schema: web; Owner: openatlas
--

ALTER TABLE ONLY web.annotation_image
ADD CONSTRAINT annotation_image_pkey PRIMARY KEY (id);


--
-- Name: entity_profile_image entity_profile_image_entity_id_key; Type: CONSTRAINT; Schema: web; Owner: openatlas
--
Expand Down Expand Up @@ -2221,6 +2281,30 @@ ALTER TABLE ONLY model.property
ADD CONSTRAINT property_range_class_code_fkey FOREIGN KEY (range_class_code) REFERENCES model.cidoc_class(code) ON UPDATE CASCADE ON DELETE CASCADE;


--
-- Name: annotation_image annotation_image_entity_id_fkey; Type: FK CONSTRAINT; Schema: web; Owner: openatlas
--

ALTER TABLE ONLY web.annotation_image
ADD CONSTRAINT annotation_image_entity_id_fkey FOREIGN KEY (entity_id) REFERENCES model.entity(id) ON UPDATE CASCADE ON DELETE SET NULL;


--
-- Name: annotation_image annotation_image_image_id_fkey; Type: FK CONSTRAINT; Schema: web; Owner: openatlas
--

ALTER TABLE ONLY web.annotation_image
ADD CONSTRAINT annotation_image_image_id_fkey FOREIGN KEY (image_id) REFERENCES model.entity(id) ON UPDATE CASCADE ON DELETE CASCADE;


--
-- Name: annotation_image annotation_image_user_id_fkey; Type: FK CONSTRAINT; Schema: web; Owner: openatlas
--

ALTER TABLE ONLY web.annotation_image
ADD CONSTRAINT annotation_image_user_id_fkey FOREIGN KEY (user_id) REFERENCES web."user"(id) ON UPDATE CASCADE ON DELETE SET NULL;


--
-- Name: entity_profile_image entity_profile_image_entity_id_fkey; Type: FK CONSTRAINT; Schema: web; Owner: openatlas
--
Expand Down
127 changes: 113 additions & 14 deletions openatlas/api/endpoints/iiif.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
import math
import mimetypes
from typing import Any

import requests
from flask import jsonify, Response, url_for, g
from flask_restful import Resource
from shapely.geometry import Polygon

from openatlas.api.resources.model_mapper import get_entity_by_id
from openatlas.api.resources.util import get_license_name
from openatlas.models.annotation import AnnotationImage
from openatlas.models.entity import Entity


class IIIFSequenceV2(Resource):
@staticmethod
def get(id_: int) -> Response:
return jsonify(
{"@context": "https://iiif.io/api/presentation/2/context.json"} |
{"@context": "http://iiif.io/api/presentation/2/context.json"} |
IIIFSequenceV2.build_sequence(get_metadata(get_entity_by_id(id_))))

@staticmethod
Expand All @@ -23,7 +26,6 @@ def build_sequence(metadata: dict[str, Any]) -> dict[str, Any]:
"@id": url_for(
'api.iiif_sequence',
id_=metadata['entity'].id,
version=2,
_external=True),
"@type": "sc:Sequence",
"label": [{
Expand All @@ -37,25 +39,30 @@ class IIIFCanvasV2(Resource):
@staticmethod
def get(id_: int) -> Response:
return jsonify(
{"@context": "https://iiif.io/api/presentation/2/context.json"} |
{"@context": "http://iiif.io/api/presentation/2/context.json"} |
IIIFCanvasV2.build_canvas(get_metadata(get_entity_by_id(id_))))

@staticmethod
def build_canvas(metadata: dict[str, Any]) -> dict[str, Any]:
entity = metadata['entity']
mime_type, _ = mimetypes.guess_type(g.files[entity.id])
return {
"@id": url_for(
'api.iiif_canvas', id_=entity.id, version=2, _external=True),
"@id": url_for('api.iiif_canvas', id_=entity.id, _external=True),
"@type": "sc:Canvas",
"label": entity.name,
"height": metadata['img_api']['height'],
"width": metadata['img_api']['width'],
"description": {
"@value": entity.description,
"@value": entity.description or '',
"@language": "en"},
"images": [IIIFImageV2.build_image(metadata)],
"related": "",
"otherContent": [{
"@id": url_for(
'api.iiif_annotation_list',
id_=entity.id,
_external=True),
"@type": "sc:AnnotationList"}],
"thumbnail": {
"@id": f'{metadata["img_url"]}/full/!200,200/0/default.jpg',
"@type": "dctypes:Image",
Expand All @@ -79,9 +86,9 @@ def build_image(metadata: dict[str, Any]) -> dict[str, Any]:
id_ = metadata['entity'].id
mime_type, _ = mimetypes.guess_type(g.files[id_])
return {
"@context": "https://iiif.io/api/presentation/2/context.json",
"@context": "http://iiif.io/api/presentation/2/context.json",
"@id":
url_for('api.iiif_image', id_=id_, version=2, _external=True),
url_for('api.iiif_image', id_=id_, _external=True),
"@type": "oa:Annotation",
"motivation": "sc:painting",
"resource": {
Expand All @@ -95,7 +102,97 @@ def build_image(metadata: dict[str, Any]) -> dict[str, Any]:
"height": metadata['img_api']['height'],
"width": metadata['img_api']['width']},
"on":
url_for('api.iiif_canvas', id_=id_, version=2, _external=True)}
url_for('api.iiif_canvas', id_=id_, _external=True)}


class IIIFAnnotationListV2(Resource):
@staticmethod
def get(id_: int) -> Response:
return jsonify(
IIIFAnnotationListV2.build_annotation_list(
get_metadata(get_entity_by_id(id_))))

@staticmethod
def build_annotation_list(metadata: dict[str, Any]) -> dict[str, Any]:
id_ = metadata['entity'].id
return {
"@context": "http://iiif.io/api/presentation/2/context.json",
"@id": url_for(
'api.iiif_annotation_list',
id_=id_,
_external=True),
"@type": "sc:AnnotationList",
"resources":
[IIIFAnnotationV2.build_annotation(metadata, anno)
for anno in AnnotationImage.get_by_file(id_)]}


class IIIFAnnotationV2(Resource):
@staticmethod
def get(id_: int, annotation_id: int) -> Response:
return jsonify(
IIIFImageV2.build_annotation(
get_metadata(get_entity_by_id(id_)),
AnnotationImage.get_by_file(annotation_id)))

@staticmethod
def build_annotation(
metadata: dict[str, Any],
anno: dict[str, Any]) -> dict[str, Any]:
id_ = metadata['entity'].id
selector = generate_selector(anno)
return {
"@context": "http://iiif.io/api/presentation/2/context.json",
"@id": url_for(
'api.iiif_annotation',
id_=id_,
annotation_id=anno['id'],
_external=True),
"@type": "oa:Annotation",
"motivation": ["oa:commenting"],
"resource": [{
"@type": "dctypes:Text",
"chars": anno['annotation'],
"format": "text/html"
}],
"on": {
"@type": "oa:SpecificResource",
"full": url_for('api.iiif_canvas', id_=id_, _external=True),
"selector": selector,
"within": {
"@id": url_for(
'api.iiif_manifest',
id_=id_, version=2,
_external=True),
"@type": "sc:Manifest"}}}


def calculate_fragment_selector_coordinates(coordinates):
coordinates_str = coordinates['coordinates']
# Splitting the coordinates string into individual values
coordinates_list = list(map(float, coordinates_str.split(',')))

# Extracting x, y, width, and height from the coordinates
x_min, y_min, x_max, y_max = min(coordinates_list[::2]), min(coordinates_list[1::2]), max(coordinates_list[::2]), max(coordinates_list[1::2])
x = x_min
y = y_min
width = x_max - x_min
height = y_max - y_min

return x, y, width, height

def generate_selector(annotation):
print(annotation)
coordinates = [
float(coord) for coord in annotation['coordinates'].split(',')]

x, y, width, height = calculate_fragment_selector_coordinates(annotation)
print(x, y, width, height)
output = {
"@type": "oa:FragmentSelector",
"value": f"xywh={x},{y},{width},{height}"}

return output


class IIIFManifest(Resource):
Expand All @@ -108,7 +205,7 @@ def get(version: int, id_: int) -> Response:
def get_manifest_version_2(id_: int) -> dict[str, Any]:
entity = get_entity_by_id(id_)
return {
"@context": "https://iiif.io/api/presentation/2/context.json",
"@context": "http://iiif.io/api/presentation/2/context.json",
"@id":
url_for(
'api.iiif_manifest',
Expand All @@ -117,9 +214,11 @@ def get_manifest_version_2(id_: int) -> dict[str, Any]:
_external=True),
"@type": "sc:Manifest",
"label": entity.name,
"metadata": [],
"metadata": [{
"label": "Title",
"value": entity.name}],
"description": [{
"@value": entity.description,
"@value": entity.description or '',
"@language": "en"}],
"license": get_license_name(entity),
"attribution": "By OpenAtlas",
Expand All @@ -144,6 +243,6 @@ def get_logo() -> dict[str, Any]:
filename=g.settings['logo_file_id'],
_external=True),
"service": {
"@context": "https://iiif.io/api/image/2/context.json",
"@context": "http://iiif.io/api/image/2/context.json",
"@id": url_for('overview', _external=True),
"profile": "https://iiif.io/api/image/2/level2.json"}}
"profile": "http://iiif.io/api/image/2/level2.json"}}
Loading

0 comments on commit 62ee720

Please sign in to comment.