From cdaf748eb876eb9aa3d3d977bb515fc5f37e70fb Mon Sep 17 00:00:00 2001 From: Glignos Date: Wed, 15 May 2019 09:43:55 +0200 Subject: [PATCH] docs: add module documentation --- MANIFEST.in | 2 + README.rst | 13 ++-- docs/api.rst | 27 ++++++- examples/demo_files/img.jpg | 0 examples/demo_files/img.png | 0 invenio_iiif/__init__.py | 138 +++++++++++++++++++++++++++++++++++- invenio_iiif/handlers.py | 4 ++ invenio_iiif/previewer.py | 15 +++- invenio_iiif/tasks.py | 11 ++- invenio_iiif/utils.py | 12 +++- setup.py | 7 +- 11 files changed, 212 insertions(+), 17 deletions(-) create mode 100644 examples/demo_files/img.jpg create mode 100644 examples/demo_files/img.png diff --git a/MANIFEST.in b/MANIFEST.in index b493d30..f5d9316 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -14,6 +14,8 @@ include *.sh include *.txt include LICENSE include pytest.ini +include examples/demo_files/img.png +include examples/demo_files/img.jpg prune docs/_build recursive-include docs *.bat recursive-include docs *.py diff --git a/README.rst b/README.rst index 5ebb74b..93661b1 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,6 @@ .. This file is part of Invenio. - Copyright (C) 2018 CERN. + Copyright (C) 2018-2019 CERN. Invenio is free software; you can redistribute it and/or modify it under the terms of the MIT License; see LICENSE file for more details. @@ -21,7 +21,12 @@ .. image:: https://img.shields.io/pypi/v/invenio-iiif.svg :target: https://pypi.org/pypi/invenio-iiif -IIIF API for Invenio. +Invenio module to deliver images complying with IIIF standard. -Further documentation is available on -https://invenio-iiif.readthedocs.io/ +Features: + +- Retrieve and serve image files using Invenio files. +- Allows to define custom REST API endpoint prefix. +- Provide celery task to create image thumbnails. + +Further documentation available: https://invenio-iiif.readthedocs.io/ diff --git a/docs/api.rst b/docs/api.rst index dfee86b..df5c8db 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -1,13 +1,36 @@ .. This file is part of Invenio. - Copyright (C) 2018 CERN. + Copyright (C) 2019 CERN. Invenio is free software; you can redistribute it and/or modify it under the terms of the MIT License; see LICENSE file for more details. - API Docs ======== .. automodule:: invenio_iiif.ext :members: + +Handlers +-------- + +.. automodule:: invenio_iiif.handlers + :members: + +Previewer +--------- + +.. automodule:: invenio_iiif.previewer + :members: + +Tasks +----- + +.. automodule:: invenio_iiif.tasks + :members: + +Utils +----- + +.. automodule:: invenio_iiif.utils + :members: diff --git a/examples/demo_files/img.jpg b/examples/demo_files/img.jpg new file mode 100644 index 0000000..e69de29 diff --git a/examples/demo_files/img.png b/examples/demo_files/img.png new file mode 100644 index 0000000..e69de29 diff --git a/invenio_iiif/__init__.py b/invenio_iiif/__init__.py index a4efe37..d91d017 100644 --- a/invenio_iiif/__init__.py +++ b/invenio_iiif/__init__.py @@ -6,7 +6,143 @@ # Invenio is free software; you can redistribute it and/or modify it # under the terms of the MIT License; see LICENSE file for more details. -"""IIIF API for Invenio.""" +"""Invenio module to serve images complying with IIIF standard. + +Invenio-IIIF integrates +`Invenio-Records-Files `_ with +`Flask-IIIF `_ to provide an endpoint for serving +images complying with the `International Image Interoperability Framework +(IIIF) `_ API standards. + +Invenio-IIIF registers the REST API endpoint provided by Flask-IIIF in the +Invenio instance through entry points. On each image request, it delegates +authorization check and file retrieval to +`Invenio-Files-REST `_ and it serves the +image after adaptation by Flask-IIIF. + +Initialization +-------------- + +First create a Flask application: + +>>> from flask import Flask +>>> app = Flask('myapp') +>>> app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://' + +Configuration +~~~~~~~~~~~~~ + +Then, You can define the IIIF API prefix by setting the Invenio configuration +``IIIF_UI_URL``. + +>>> app.config.update(IIIF_UI_URL='/iiif-demo') + +Usage +----- + +The example below will demonstrate how Invenio-IIIF works with a simple image. +First, initialize the needed Invenio extensions: + +>>> from invenio_db import InvenioDB, db +>>> from invenio_files_rest import InvenioFilesREST +>>> ext_db = InvenioDB(app) +>>> ext_files_rest = InvenioFilesREST(app) +>>> app.app_context().push() +>>> db.create_all() + +Add sample images +~~~~~~~~~~~~~~~~~ + +To be able to add the files to Invenio, let's create a default location first: + +>>> import tempfile +>>> tmpdir = tempfile.mkdtemp() +>>> from invenio_files_rest.models import Location +>>> loc = Location(name='local', uri=tmpdir, default=True) +>>> db.session.add(loc) +>>> db.session.commit() + +And a bucket with the previously created location: + +>>> from invenio_files_rest.models import Bucket +>>> b1 = Bucket.create(loc) + +Now, add a few sample images: + +>>> import os +>>> from invenio_files_rest.models import ObjectVersion +>>> demo_files_path = 'examples/demo_files' +>>> demo_files = ( +... 'img.jpg', +... 'img.png') +>>> for f in demo_files: +... with open(os.path.join(demo_files_path, f), 'rb') as fp: +... img = ObjectVersion.create(b1, f, stream=fp) +>>> db.session.commit() + +Serving an image +~~~~~~~~~~~~~~~~ + +While Flask-IIIF requires the UUID of the image to retrieve as part of the URL, +Invenio needs the bucket id, version id and the key of the file so that it can +be retrieved via `Invenio-Files-REST `_. +Invenio-IIIF provides an utility to prepare such URLs, e.g. +``/v2//``, and convert the ``uuid`` parameter to a concatenation of +``::``. + +Given a previously created image object: + +>>> img_obj = ObjectVersion.get_versions(bucket=b1, +... key=demo_files[1]).first() + +you can create the corresponding IIIF URL: + +>>> from invenio_iiif.utils import ui_iiif_image_url +>>> image_url = ui_iiif_image_url( +... obj=img_obj, version='v2', region='full', size='full', rotation=0, +... quality='default', image_format='png') + +The result will be +``/iiif-demov2/::img.png/full/full/0/default.png`` + +PDF cover +~~~~~~~~~ + +If the file is a PDF and ImageMagick is installed in your system, then the +module can extract the first page of the PDF, create a ``png`` from it and +serve it as cover page. + +Authorization +~~~~~~~~~~~~~ + +Permissions to retrieve the requested images are delegated to +`Invenio-Files-REST `_. At each request, +authorization is checked to ensure that the user has sufficient privileges. + +Preview +~~~~~~~ + +Invenio-IIIF can be used in a combination with +`Invenio-Previewer `_ to preview images. +It provides an extension to preview the most common image formats and it is +exposed via the entry point named ``iiif_image``. + +The template used to render the image can be configured with the configuration +``IIIF_PREVIEW_TEMPLATE``. + +Thumbnails +~~~~~~~~~~ + +The module can precache thumbnails of requested images. It provides a celery +task that will fetch a given image and resize it to create a thumbnail. It is +then cached so it can be served efficiently. + +.. code-block:: python + + from invenio_iiif.tasks import create_thumbnail + create_thumbnail(image_key, '250') + +""" from __future__ import absolute_import, print_function diff --git a/invenio_iiif/handlers.py b/invenio_iiif/handlers.py index 8dc071c..32d44db 100644 --- a/invenio_iiif/handlers.py +++ b/invenio_iiif/handlers.py @@ -40,6 +40,10 @@ def protect_api(uuid=None, **kwargs): def image_opener(key): """Handler to locate file based on key. + .. note:: + If the file is a PDF then only the first page will be + returned as an image. + :param key: A key encoded in the format "::". :returns: A file-like object. """ diff --git a/invenio_iiif/previewer.py b/invenio_iiif/previewer.py index 9da6da4..6d83013 100644 --- a/invenio_iiif/previewer.py +++ b/invenio_iiif/previewer.py @@ -27,13 +27,24 @@ def can_preview(file): - """Determine if the given file can be previewed.""" + """Determine if the given file can be previewed by its extension. + + :param file: The file to be previewed. + :returns: Boolean + """ supported_extensions = ('.jpg', '.jpeg', '.png', '.tif', '.tiff') return file.has_extensions(*supported_extensions) def preview(file): - """Render appropriate template with embed flag.""" + """Render appropriate template with embed flag. + + .. note:: + Any non .png image is treated as .jpg + + :param file: The file to be previewed. + :returns: Template with the preview of the provided file. + """ params = deepcopy(current_app.config['IIIF_PREVIEWER_PARAMS']) if 'image_format' not in params: params['image_format'] = \ diff --git a/invenio_iiif/tasks.py b/invenio_iiif/tasks.py index 838bf80..8c15a40 100644 --- a/invenio_iiif/tasks.py +++ b/invenio_iiif/tasks.py @@ -16,7 +16,16 @@ @shared_task(ignore_result=True) def create_thumbnail(uuid, thumbnail_width): - """Create the thumbnail for an image.""" + """Create the thumbnail for an image. + + .. note:: + + The purpose of this function is solely to cache + thumbnails for future use. + + :param uuid: UUID of the file to be retrieved. + :param thumbnail_width: Width of the thumbnail to be created. + """ # size = '!' + thumbnail_width + ',' size = thumbnail_width + ',' # flask_iiif doesn't support ! at the moment thumbnail = IIIFImageAPI.get('v2', uuid, size, 0, 'default', 'jpg') diff --git a/invenio_iiif/utils.py b/invenio_iiif/utils.py index 7994e2e..46a7e92 100644 --- a/invenio_iiif/utils.py +++ b/invenio_iiif/utils.py @@ -16,7 +16,11 @@ def iiif_image_key(obj): - """Generate the IIIF image key.""" + """Generate a unique IIIF image key, using the images DB location. + + :param obj: File object instance. + :returns: Image key 'u'(str) + """ if isinstance(obj, ObjectVersion): bucket_id = obj.bucket_id version_id = obj.version_id @@ -34,7 +38,11 @@ def iiif_image_key(obj): def ui_iiif_image_url(obj, version='v2', region='full', size='full', rotation=0, quality='default', image_format='png'): - """Generate IIIF image URL from the UI application.""" + """Generate IIIF image URL from the UI application. + + :param obj: File object instance. + :returns: URL to retrieve the processed image from. + """ return u'{prefix}{version}/{identifier}/{region}/{size}/{rotation}/' \ u'{quality}.{image_format}'.format( prefix=current_app.config['IIIF_UI_URL'], diff --git a/setup.py b/setup.py index 06088fb..c46adcf 100644 --- a/setup.py +++ b/setup.py @@ -18,14 +18,11 @@ tests_require = [ 'check-manifest>=0.25', 'coverage>=4.0', - # FIXME: Remove elasticsearch once pytest-invenio have fixed the es import. - 'elasticsearch>=5.0.0', - 'elasticsearch-dsl>=5.0.0', + 'urllib3>=1.21.1,<1.25', 'invenio-db>=1.0.0b3', 'isort>=4.3.4', 'pydocstyle>=1.0.0', - 'pytest-cov>=1.8.0', - 'pytest-invenio>=1.0.0', + 'pytest-invenio>=1.1.1', 'pytest-pep8>=1.0.6', 'pytest>=3.7.0', ]