From 9ebbb9bff97e92997946f0b1689a01f2ed629c1c Mon Sep 17 00:00:00 2001 From: bkoschicek Date: Tue, 26 Sep 2023 15:57:29 +0200 Subject: [PATCH] moved to api and fixed some issue --- openatlas/api/endpoints/iiif.py | 83 +++++++++++++++++++++++++++++ openatlas/api/routes.py | 6 +++ openatlas/display/util.py | 11 ++++ openatlas/views/admin.py | 4 +- openatlas/views/file.py | 94 ++------------------------------- 5 files changed, 107 insertions(+), 91 deletions(-) create mode 100644 openatlas/api/endpoints/iiif.py diff --git a/openatlas/api/endpoints/iiif.py b/openatlas/api/endpoints/iiif.py new file mode 100644 index 000000000..c4459347f --- /dev/null +++ b/openatlas/api/endpoints/iiif.py @@ -0,0 +1,83 @@ +import requests +from flask import request, jsonify, Response +from flask_restful import Resource + +from api.resources.util import get_license_name +from models.entity import Entity +from openatlas import app + + +def getManifest(id_): + entity = Entity.get_by_id(id_) + url_root = app.config['IIIF_URL'] or f"{request.url_root}iiif/" + # get metadata from the image api + req = requests.get( + f"{url_root}{app.config['IIIF_PREFIX']}{id_}/info.json") + image_api = req.json() + iiif_id = f"{url_root}iiif/{id_}" + manifest = { + "@context": "http://iiif.io/api/presentation/2/context.json", + "@id": f"{request.base_url}", + "@type": "sc:Manifest", + "label": entity.name, + "metadata": [], + "description": [{ + "@value": entity.description, + "@language": "en"}], + "thumbnail": { + "@id": f"{iiif_id}/full/250,250/0/default.jpg", + "service": { + "@context": "http://iiif.io/api/image/2/context.json", + "@id": iiif_id, + "profile": "http://iiif.io/api/image/2/level1.json" + }}, + "license": get_license_name(entity), + "attribution": "By OpenAtlas", + "sequences": [{ + "@id": "http://c8b09ce6-df6d-4d5e-9eba-17507dc5c185", + "@type": "sc:Sequence", + "label": [{ + "@value": "Normal Sequence", + "@language": "en"}], + "canvases": [{ + "@id": "http://251a31df-761d-46df-85c3-66cb967b8a67", + "@type": "sc:Canvas", + "label": entity.name, + "height": image_api['height'], + "width": image_api['width'], + "description": { + "@value": entity.description, + "@language": "en"}, + "images": [{ + "@context": + "http://iiif.io/api/presentation/2/context.json", + "@id": "http://a0a3ec3e-2084-4253-b0f9-a5f87645e15d", + "@type": "oa:Annotation", + "motivation": "sc:painting", + "resource": { + "@id": f"{iiif_id}/full/full/0/default.jpg", + "@type": "dctypes:Image", + "format": "image/jpeg", + "service": { + "@context": + "http://iiif.io/api/image/2/context.json", + "@id": iiif_id, + "profile": image_api['profile'] + }, + "height": image_api['height'], + "width": image_api['width']}, + "on": "http://251a31df-761d-46df-85c3-66cb967b8a67"}], + "related": ""}]}], + "structures": []} + return manifest + + +class IIIFManifest(Resource): + @staticmethod + def get(id_: int) -> Response: + # content = gzip.compress(json.dumps(getManifest(id_)).encode( + # 'utf8'), 5) + # response = Response(content) + # response.headers['Content-length'] = len(content) + # response.headers['Content-Encoding'] = 'gzip' + return jsonify(getManifest(id_)) diff --git a/openatlas/api/routes.py b/openatlas/api/routes.py index 7228bbfc3..16aed8c58 100644 --- a/openatlas/api/routes.py +++ b/openatlas/api/routes.py @@ -1,5 +1,6 @@ from flask_restful import Api +from api.endpoints.iiif import IIIFManifest from openatlas.api.endpoints.content import ClassMapping, \ GetContent, SystemClassCount from openatlas.api.endpoints.special import GetGeometricEntities, \ @@ -93,3 +94,8 @@ def add_routes_v03(api: Api) -> None: DisplayImage, '/display/', endpoint='display') + + api.add_resource( + IIIFManifest, + '/iiif_manifest/', + endpoint='iiif_manifest') diff --git a/openatlas/display/util.py b/openatlas/display/util.py index 2b4943a22..865ebc7ba 100644 --- a/openatlas/display/util.py +++ b/openatlas/display/util.py @@ -4,6 +4,7 @@ import os import re import smtplib +import subprocess from datetime import datetime, timedelta from email.header import Header from email.mime.text import MIMEText @@ -768,3 +769,13 @@ def check_iiif_file_exist(id_: int) -> bool: file_to_check = (Path(app.config['IIIF_DIR']) / app.config['IIIF_PREFIX'] / str(id_)) return file_to_check.is_file() + + +def convert_image_to_iiif(id_): + path = Path(app.config['IIIF_DIR']) / app.config['IIIF_PREFIX'] / str(id_) + vips = "vips" if os.name == 'posix' else "vips.exe" + command = \ + (f"{vips} tiffsave {get_file_path(id_)} {path} " + f"--tile --pyramid --compression deflate " + f"--tile-width 256 --tile-height 256") + subprocess.Popen(command, shell=True) diff --git a/openatlas/views/admin.py b/openatlas/views/admin.py index 1240d7914..d4342f988 100644 --- a/openatlas/views/admin.py +++ b/openatlas/views/admin.py @@ -38,7 +38,7 @@ from openatlas.models.settings import Settings from openatlas.models.type import Type from openatlas.models.user import User -from openatlas.views.file import convert_image_to_iiif +from display.util import convert_image_to_iiif @app.route('/admin', methods=['GET', 'POST'], strict_slashes=False) @@ -781,7 +781,7 @@ def get_disk_space_info() -> Optional[dict[str, Any]]: @app.route('/admin/admin_convert_all_to_iiif') -@required_group('admin') +@required_group('manager') def admin_convert_all_to_iiif() -> Response: for entity in Entity.get_by_class('file'): if entity.id in g.file_stats \ diff --git a/openatlas/views/file.py b/openatlas/views/file.py index fd1cc7613..0bf505448 100644 --- a/openatlas/views/file.py +++ b/openatlas/views/file.py @@ -1,20 +1,14 @@ -import gzip -import json -import os -import subprocess -from pathlib import Path from typing import Any, Union, Optional -import requests from flask import g, render_template, request, send_from_directory, url_for, \ - jsonify + redirect from flask_babel import lazy_gettext as _ -from flask_cors import cross_origin from werkzeug.utils import redirect from werkzeug.wrappers import Response +from display.util import convert_image_to_iiif, required_group from openatlas import app -from openatlas.display.util import required_group, get_file_path +from openatlas.display.util import required_group from openatlas.forms.form import get_table_form from openatlas.models.entity import Entity @@ -76,93 +70,15 @@ def file_add(id_: int, view: str) -> Union[str, Response]: f"{_('link')} {_(view)}"]) -@app.route('/file/iiif/', methods=['GET']) +@app.route('/file/convert_iiif/', methods=['GET']) @required_group('contributor') def make_iiif_available(id_: int): convert_image_to_iiif(id_) return redirect(url_for('view', id_=id_)) -def convert_image_to_iiif(id_): - path = Path(app.config['IIIF_DIR']) / app.config['IIIF_PREFIX'] / str(id_) - vips = "vips" if os.name == 'posix' else "vips.exe" - command = \ - (f"{vips} tiffsave {get_file_path(id_)} {path} " - f"--tile --pyramid --compression deflate " - f"--tile-width 256 --tile-height 256") - subprocess.Popen(command, shell=True) - - -def getManifest(id_): - entity = Entity.get_by_id(id_) - url_root = app.config['IIIF_SERVER'] or request.url_root - - # get metadata from the image api - req = requests.get( - f"{url_root}iiif/{app.config['IIIF_PREFIX']}{id_}/info.json") - image_api = req.json() - print(image_api) - manifest = { - "@context": "http://iiif.io/api/presentation/2/context.json", - "@id": f"{request.base_url}", - "@type": "sc:Manifest", - "label": entity.name, - "metadata": [], - "description": [{ - "@value": entity.description, - "@language": "en"}], - "license": "https://creativecommons.org/licenses/by/3.0/", - "attribution": "By OpenAtlas", - "sequences": [{ - "@id": "http://c8b09ce6-df6d-4d5e-9eba-17507dc5c185", - "@type": "sc:Sequence", - "label": [{ - "@value": "Normal Sequence", - "@language": "en"}], - "canvases": [{ - "@id": "http://251a31df-761d-46df-85c3-66cb967b8a67", - "@type": "sc:Canvas", - "label": entity.name, - "height": 450, - "width": 600, - "description": { - "@value": entity.description, - "@language": "en"}, - "images": [{ - "@context": "http://iiif.io/api/presentation/2/context.json", - "@id": "http://a0a3ec3e-2084-4253-b0f9-a5f87645e15d", - "@type": "oa:Annotation", - "motivation": "sc:painting", - "resource": { - "@id": f"{url_root}iiif/{id_}/full/full/0/default.jpg", - "@type": "dctypes:Image", - "format": "image/jpeg", - "service": { - "@context": "http://iiif.io/api/image/2/context.json", - "@id": f"{url_root}iiif/{id_}", - "profile": image_api['profile'] - }, - "height": 450, - "width": 600}, - "on": "http://251a31df-761d-46df-85c3-66cb967b8a67"}], - "related": ""}]}], - "structures": []} - - return manifest - - -@app.route('/iiif_manifest/') -@cross_origin() -def iiif_manifest(id_: int): - # content = gzip.compress(json.dumps(getManifest(id_)).encode('utf8'), 5) - # response = Response(content) - # response.headers['Content-length'] = len(content) - # response.headers['Content-Encoding'] = 'gzip' - return jsonify(getManifest(id_)) - - @app.route('/iiif/', methods=['GET']) @app.route('/iiif//', methods=['GET']) @required_group('contributor') def view_iiif(id_: int, prefix: Optional[str] = None): - return redirect(url_for('view', id_=id_)) + return redirect(url_for('api.iiif_manifest', id_=id_))