Skip to content

Commit

Permalink
Image annotation with Bernhard
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexanderWatzinger committed Jan 11, 2024
1 parent b90905f commit ca7d9ee
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 68 deletions.
92 changes: 43 additions & 49 deletions openatlas/api/endpoints/iiif.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import mimetypes
from typing import Any

Expand All @@ -7,16 +9,16 @@

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.annotation import Annotation
from openatlas.models.entity import Entity


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

@staticmethod
def build_sequence(metadata: dict[str, Any]) -> dict[str, Any]:
Expand All @@ -29,16 +31,15 @@ def build_sequence(metadata: dict[str, Any]) -> dict[str, Any]:
"label": [{
"@value": "Normal Sequence",
"@language": "en"}],
"canvases": [
IIIFCanvasV2.build_canvas(metadata)]}
"canvases": [IIIFCanvas.build_canvas(metadata)]}


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

@staticmethod
def build_canvas(metadata: dict[str, Any]) -> dict[str, Any]:
Expand All @@ -53,12 +54,12 @@ def build_canvas(metadata: dict[str, Any]) -> dict[str, Any]:
"description": {
"@value": entity.description or '',
"@language": "en"},
"images": [IIIFImageV2.build_image(metadata)],
"images": [IIIFImage.build_image(metadata)],
"related": "",
"otherContent": [{
"@id": url_for(
'api.iiif_annotation_list',
id_=entity.id,
image_id=entity.id,
_external=True),
"@type": "sc:AnnotationList"}],
"thumbnail": {
Expand All @@ -73,20 +74,19 @@ def build_canvas(metadata: dict[str, Any]) -> dict[str, Any]:
"profile": metadata['img_api']['profile']}}}


class IIIFImageV2(Resource):
class IIIFImage(Resource):
@staticmethod
def get(id_: int) -> Response:
return jsonify(
IIIFImageV2.build_image(get_metadata(get_entity_by_id(id_))))
IIIFImage.build_image(get_metadata(get_entity_by_id(id_))))

@staticmethod
def build_image(metadata: dict[str, Any]) -> dict[str, Any]:
id_ = metadata['entity'].id
mime_type, _ = mimetypes.guess_type(g.files[id_])
return {
"@context": "http://iiif.io/api/presentation/2/context.json",
"@id":
url_for('api.iiif_image', id_=id_, _external=True),
"@id": url_for('api.iiif_image', id_=id_, _external=True),
"@type": "oa:Annotation",
"motivation": "sc:painting",
"resource": {
Expand All @@ -99,79 +99,73 @@ def build_image(metadata: dict[str, Any]) -> dict[str, Any]:
"profile": metadata['img_api']['profile']},
"height": metadata['img_api']['height'],
"width": metadata['img_api']['width']},
"on":
url_for('api.iiif_canvas', id_=id_, _external=True)}
"on": url_for('api.iiif_canvas', id_=id_, _external=True)}


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

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


class IIIFAnnotationV2(Resource):
class IIIFAnnotation(Resource):
@staticmethod
def get(id_: int, annotation_id: int) -> Response:
def get(annotation_id: int) -> Response:
return jsonify(
IIIFImageV2.build_annotation(
get_metadata(get_entity_by_id(id_)),
AnnotationImage.get_by_file_as_dict(annotation_id)))
IIIFAnnotation.build_annotation(
Annotation.get_by_id(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)
def build_annotation(annotation: Annotation) -> dict[str, Any]:
selector = generate_selector(annotation.coordinates)
return {
"@context": "http://iiif.io/api/presentation/2/context.json",
"@id": url_for(
'api.iiif_annotation',
id_=id_,
annotation_id=anno['id'],
annotation_id=annotation.id,
_external=True),
"@type": "oa:Annotation",
"motivation": ["oa:commenting"],
"resource": [{
"@type": "dctypes:Text",
"chars": anno['annotation'],
"format": "text/html"
}],
"chars": annotation.annotation,
"format": "text/html"}],
"on": {
"@type": "oa:SpecificResource",
"full": url_for('api.iiif_canvas', id_=id_, _external=True),
"full": url_for(
'api.iiif_canvas',
id_=annotation.image_id,
_external=True),
"selector": selector,
"within": {
"@id": url_for(
'api.iiif_manifest',
id_=id_, version=2,
id_=annotation.image_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(',')))
coordinates_list = list(map(float, coordinates.split(',')))

# Extracting x, y, width, and height from the coordinates
x_min, y_min,x_max, y_max = (
x_min, y_min, x_max, y_max = (
min(coordinates_list[::2]),
min(coordinates_list[1::2]),
max(coordinates_list[::2]),
Expand All @@ -183,8 +177,8 @@ def calculate_fragment_selector_coordinates(coordinates):
return x, y, width, height


def generate_selector(annotation):
x, y, width, height = calculate_fragment_selector_coordinates(annotation)
def generate_selector(coordinates: str):
x, y, width, height = calculate_fragment_selector_coordinates(coordinates)
return {
"@type": "oa:FragmentSelector",
"value": f"xywh={x},{y},{width},{height}"}
Expand Down Expand Up @@ -218,7 +212,7 @@ def get_manifest_version_2(id_: int) -> dict[str, Any]:
"license": get_license_name(entity),
"logo": get_logo(),
"sequences": [
IIIFSequenceV2.build_sequence(get_metadata(entity))],
IIIFSequence.build_sequence(get_metadata(entity))],
"structures": []}


Expand Down
18 changes: 9 additions & 9 deletions openatlas/api/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
GetEntitiesLinkedToEntity, GetEntity, GetLatest, GetQuery, GetTypeEntities,
GetTypeEntitiesAll)
from openatlas.api.endpoints.iiif import (
IIIFAnnotationListV2, IIIFAnnotationV2, IIIFCanvasV2, IIIFImageV2,
IIIFManifest, IIIFSequenceV2)
IIIFAnnotationList, IIIFAnnotation, IIIFCanvas, IIIFImage,
IIIFManifest, IIIFSequence)
from openatlas.api.endpoints.special import (
ExportDatabase, GetGeometricEntities, GetSubunits)
from openatlas.api.endpoints.type import (
Expand Down Expand Up @@ -116,22 +116,22 @@ def display_routes(api: Api) -> None:
'/iiif_manifest/<int:version>/<int:id_>',
endpoint='iiif_manifest')
api.add_resource(
IIIFAnnotationListV2,
'/iiif_annotation_list/<int:id_>.json',
IIIFAnnotationList,
'/iiif_annotation_list/<int:image_id>.json',
endpoint='iiif_annotation_list')
api.add_resource(
IIIFAnnotationV2,
'/iiif_annotation/<int:id_>/<int:annotation_id>.json',
IIIFAnnotation,
'/iiif_annotation/<int:annotation_id>.json',
endpoint='iiif_annotation')
api.add_resource(
IIIFImageV2,
IIIFImage,
'/iiif_image/<int:id_>.json',
endpoint='iiif_image')
api.add_resource(
IIIFCanvasV2,
IIIFCanvas,
'/iiif_canvas/<int:id_>.json',
endpoint='iiif_canvas')
api.add_resource(
IIIFSequenceV2,
IIIFSequence,
'/iiif_sequence/<int:id_>.json',
endpoint='iiif_sequence')
10 changes: 5 additions & 5 deletions openatlas/models/annotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from openatlas.database.annotation import AnnotationImage as Db


class AnnotationImage:
class Annotation:

def __init__(self, data: dict[str, Any]) -> None:
self.id = data['id']
Expand All @@ -31,12 +31,12 @@ def delete(self) -> None:
Db.delete(self.id)

@staticmethod
def get_by_id(id_: int) -> AnnotationImage:
return AnnotationImage(Db.get_by_id(id_))
def get_by_id(id_: int) -> Annotation:
return Annotation(Db.get_by_id(id_))

@staticmethod
def get_by_file(image_id: int) -> list[AnnotationImage]:
return [AnnotationImage(row) for row in Db.get_by_file(image_id)]
def get_by_file(image_id: int) -> list[Annotation]:
return [Annotation(row) for row in Db.get_by_file(image_id)]

@staticmethod
def get_by_file_as_dict(image_id: int) -> list[dict[str, Any]]:
Expand Down
10 changes: 5 additions & 5 deletions openatlas/views/annotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from openatlas.display.util import (
format_date, get_file_path, is_authorized, link, required_group)
from openatlas.forms.field import SubmitField, TableField
from openatlas.models.annotation import AnnotationImage
from openatlas.models.annotation import Annotation
from openatlas.models.entity import Entity


Expand All @@ -44,14 +44,14 @@ def annotation_insert(id_: int) -> Union[str, Response]:
form = AnnotationForm()
form.annotated_entity.filter_ids = [image.id]
if form.validate_on_submit():
AnnotationImage.insert(
Annotation.insert(
image_id=id_,
coordinates=form.coordinate.data,
entity_id=form.annotated_entity.data,
annotation=form.annotation.data)
return redirect(url_for('annotation_insert', id_=image.id))
table = None
if annotations := AnnotationImage.get_by_file(image.id):
if annotations := Annotation.get_by_file(image.id):
rows = []
for annotation in annotations:
delete = ''
Expand Down Expand Up @@ -95,7 +95,7 @@ def annotation_insert(id_: int) -> Union[str, Response]:
@app.route('/annotation_update/<int:id_>', methods=['GET', 'POST'])
@required_group('contributor')
def annotation_update(id_: int) -> str | Response:
annotation = AnnotationImage.get_by_id(id_)
annotation = Annotation.get_by_id(id_)
form = AnnotationUpdateForm()
form.annotated_entity.filter_ids = [annotation.image_id]
if form.validate_on_submit():
Expand All @@ -117,7 +117,7 @@ def annotation_update(id_: int) -> str | Response:
@app.route('/annotation_delete/<int:id_>')
@required_group('contributor')
def annotation_delete(id_: int) -> Response:
annotation = AnnotationImage.get_by_id(id_)
annotation = Annotation.get_by_id(id_)
if current_user.group == 'contributor' \
and annotation.user_id != current_user.id:
abort(403)
Expand Down

0 comments on commit ca7d9ee

Please sign in to comment.