Skip to content

Commit

Permalink
docs: add module documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
Glignos authored and ntarocco committed May 21, 2019
1 parent 5f719f6 commit cdaf748
Show file tree
Hide file tree
Showing 11 changed files with 212 additions and 17 deletions.
2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 9 additions & 4 deletions README.rst
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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/
27 changes: 25 additions & 2 deletions docs/api.rst
Original file line number Diff line number Diff line change
@@ -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:
Empty file added examples/demo_files/img.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file added examples/demo_files/img.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
138 changes: 137 additions & 1 deletion invenio_iiif/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://invenio-records-files.rtfd.io>`_ with
`Flask-IIIF <https://flask-iiif.rtfd.io>`_ to provide an endpoint for serving
images complying with the `International Image Interoperability Framework
(IIIF) <https://iiif.io/>`_ 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 <https://invenio-files-rest.rtfd.io>`_ 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 <https://invenio-files-rest.rtfd.io>`_.
Invenio-IIIF provides an utility to prepare such URLs, e.g.
``/v2/<uuid>/<path>``, and convert the ``uuid`` parameter to a concatenation of
``<bucket_id>:<version_id>:<key>``.
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/<bucket_id>:<version_id>: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 <https://invenio-files-rest.rtfd.io>`_. 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 <https://invenio-previewer.rtfd.io>`_ 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

Expand Down
4 changes: 4 additions & 0 deletions invenio_iiif/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 "<bucket>:<version>:<object_key>".
:returns: A file-like object.
"""
Expand Down
15 changes: 13 additions & 2 deletions invenio_iiif/previewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'] = \
Expand Down
11 changes: 10 additions & 1 deletion invenio_iiif/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
12 changes: 10 additions & 2 deletions invenio_iiif/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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'],
Expand Down
7 changes: 2 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
]
Expand Down

0 comments on commit cdaf748

Please sign in to comment.