Usage

Integration of records and files for Invenio.

Invenio-Records-Files provides basic API for integrating Invenio-Records and Invenio-Files-REST.

Initialization

First create a Flask application:

>>> from flask import Flask
>>> app = Flask('myapp')
>>> app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'

Records-Files has no Flask extension, however it is dependent on Invenio-Records and Invenio-Files-REST which must be initialized first:

>>> from invenio_db import InvenioDB
>>> ext_db = InvenioDB(app)
>>> from invenio_records import InvenioRecords
>>> from invenio_files_rest import InvenioFilesREST
>>> ext_filesrest = InvenioFilesREST(app)
>>> ext_records = InvenioRecords(app)

In order for the following examples to work, you need to work within a Flask application context so let’s push one:

>>> ctx = app.app_context()
>>> ctx.push()

Also, for the examples to work you need to create the database and tables (note, in this example you use an in-memory SQLite database):

>>> from invenio_db import db
>>> db.create_all()

Lastly, since you’re managing files, you need to create a default location. Here you will create a location in a temporary directory:

>>> import tempfile
>>> tmppath = tempfile.mkdtemp()
>>> from invenio_files_rest.models import Location
>>> db.session.add(Location(name='default', uri=tmppath, default=True))
>>> db.session.commit()

Creating a record

Import Invenio-Records-Files basic API invenio_records_files.api.Record:

>>> from invenio_records_files.api import Record

This Record class has a special property files through which you can access and create files. By default the class creates a bucket when you create a record:

>>> record = Record.create({})
>>> len(record.files)
0

You can also just create a record without an associated bucket:

>>> record_nobucket = Record.create({}, with_bucket=False)
>>> record_nobucket.files is None
True

Creating files

You are now ready to create you first file using the Invenio-Records-Files API:

>>> from six import BytesIO
>>> record.files['hello.txt'] = BytesIO(b'Hello, World')

In the above example you created a file named hello.txt as a new object in the record bucket.

Accessing files

You can access the above file through the same API:

>>> len(record.files)
1
>>> 'hello.txt' in record.files
True
>>> fileobj = record.files['hello.txt']
>>> print(fileobj.key)
hello.txt

Metadata for files

Besides creating files you can also assign metadata to files:

>>> fileobj['filetype'] = 'txt'
>>> print(record.files['hello.txt']['filetype'])
txt

Certain key names are however reserved:

>>> fileobj['key'] = 'test'
Traceback (most recent call last):
  ...
KeyError: 'key'

The reserved key names are all the properties which already exist in invenio_files_rest.models.ObjectVersion.

You can however still use the reserved keys for getting metadata:

>>> print(fileobj['key'])
hello.txt

Dumping files

You can make a dictionary of all files:

>>> dump = record.files.dumps()
>>> for k in sorted(dump[0].keys()):
...     print(k)
bucket
checksum
file_id
filetype
key
size
version_id

Retrieve files from a record

Invenio-Records-Files provides an utility to retrieve files of a given record.

>>> from invenio_records_files.utils import record_file_factory
>>> fileobj = record_file_factory(None, record, 'hello.txt')
>>> print(fileobj.key)
hello.txt

If the file does not exist or the record class has no files property, the factory will return None:

>>> fileobj = record_file_factory(None, record, 'invalid')
>>> fileobj is None
True

Some other Invenio modules such as Invenio-Previewer already uses it to programmatically access record’s files.

Integration with Invenio REST API

Invenio-Records-Files provides REST endpoints to retrieve or upload the files of a record:

# Upload a file named example.txt to the record with pid of 1
$ curl -X PUT http://localhost:5000/api/records/1/files/example.txt \
       -H "Content-Type: application/octet-stream" \
       --data-binary @example.txt

# Get the list of files for this record
$ curl -X GET http://localhost:5000/api/records/1/files/

# Download the file named ``example.txt`` of this record
$ curl -X GET http://localhost:5000/api/records/1/files/example.txt \
       -o example.txt

Invenio-Records-Files provides the same REST endpoints for bucket and objects available in Invenio-Files-REST, by implicitly injecting the record’s bucket ID to the request.

For example given the following configuration:

# Invenio-Records-REST
RECORDS_REST_ENDPOINTS = {
    recid: {
        # ...,
        item_route='/records/<pid(recid):pid_value>',
        #...,
    },
    docid: {
        # ...,
        item_route='/documents/<pid(docid):pid_value>',
        #...,
    }
}
# Invenio-Records-Files
RECORDS_FILES_REST_ENDPOINTS = {
    'RECORDS_REST_ENDPOINTS': {
        'recid': '/files',
        'docid': '/doc-files',
    },
    'DEPOSIT_REST_ENDPOINTS': {
        'depid': '/deposit-files,
    }
}

You can access the files of a record with PID 1 using the URL /api/records/1/files or of a document with PID 123 using the URL /api/documents/123/doc-files.

You can access a specific file, for instance example.txt, with the following URL /api/records/1/files/example.txt.

Invenio-Records-Files endpoint offers the same functionality provided by Invenio-Files-REST API. More information about handling files through the REST API can be found here.

Integration with Invenio-Records-UI

If you are using Invenio-Records-UI, you can easily add new views by defining new endpoints into your RECORDS_UI_ENDPOINTS configuration. In particular, you can add the file_download_ui endpoint:

RECORDS_UI_ENDPOINTS = dict(
    recid=dict(
        # ...
        route='/records/<pid_value/files/<filename>',
        view_imp='invenio_records_files.utils:file_download_ui',
        record_class='invenio_records_files.api:Record',
    )
)