Usage¶
Invenio module for generating sequences.
Invenio-SequenceGenerator is a secondary component of Invenio, responsible for automatically generating identifiers in a safe manner (i.e. making sure that every time a client requests a new identifier of a specific form, the server will provide him with a new unique identifier).
Conventions¶
Conventions regarding template strings of sequences:
They must contain exactly one placeholder for the counter, named ‘counter’, which possibly contains conversion and/or format options.
Correct False 'File {counter}'
'File {count}'
'{counter}: File'
'File'
'{counter:02d}: File'
'File {counter:invalid_format}'
Note
You can format the counter (and all other placeholders) with all the formatting options provided by Python strings.
They can contain an optional placeholder for a parent sequence, named as the parent sequence, defined when the Sequence.create API function was called.
Correct False PL = 'Playlist {counter}'
'{PL}-{FL} {counter}'
FL = '{PL}: {counter}'
'{INVALID}: {counter}'
For a more in-depth example, see Hierarchical identifiers.
They can contain an arbitrary number of user-defined placeholders, which must be always passed as keyword arguments on each instantiation. No exception is raised in case of redundant keyword arguments.
You can find more examples in User-defined keywords.
Initialization¶
First create a Flask application:
>>> from flask import Flask
>>> app = Flask('myapp')
>>> app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
Then initialise the Invenio-DB extension:
>>> from invenio_db import InvenioDB
>>> ext_db = InvenioDB(app)
Also, initialise the Invenio-SequenceGenerator extension:
>>> from invenio_sequencegenerator.ext import InvenioSequenceGenerator
>>> ext_seq = InvenioSequenceGenerator(app)
In order for the following examples to work, you need to work within an Flask application context so let’s push one:
>>> ctx = app.app_context()
>>> ctx.push()
Also, for the examples to work we need to create the database and tables (note, in this example we use an in-memory SQLite database):
>>> from invenio_db import db
>>> db.create_all()
Template definition¶
The first thing to do is to define your sequence (i.e. the specific form of your identifiers):
>>> from invenio_sequencegenerator.api import Template
>>> template = Template.create('ID', '{counter}-file')
Simple counters¶
Next, you have to get a sequence based on the aforementioned template. During this tutorial, this will be referred to as ‘instantiation’.
>>> from invenio_sequencegenerator.api import Sequence
>>> seq = Sequence(template)
Then you can request new identifiers from the sequence defined above:
>>> seq.next()
'0-file'
>>> seq.next()
'1-file'
>>> seq.next()
'2-file'
Note
By default, counters start from 0 and are incremented by 1.
Advanced counters¶
You can also specify the initial counter and increment:
>>> tpl = Template.create('ID2', '{counter:02d}-file', start=10, step=10)
>>> seq = Sequence(tpl)
>>> seq.next()
'10-file'
>>> seq.next()
'20-file'
>>> seq.next()
'30-file'
User-defined keywords¶
Consider the case where you need to generate identifiers for files of different categories. Thus, your template definition should look like this:
>>> cat = Template.create('KW', '{category}: File {counter:03d}', start=1)
Next, instantiate the template for specific categories:
Note
Keyword arguments must be always specified on instantiation.
>>> photos = Sequence(cat, category='PHOTOS')
>>> photos.next()
'PHOTOS: File 001'
>>> photos.next()
'PHOTOS: File 002'
>>> videos = Sequence(cat, category='VIDEOS')
>>> videos.next()
'VIDEOS: File 001'
>>> videos.next()
'VIDEOS: File 002'
>>> Sequence('invalid')
Traceback (most recent call last):
...
SequenceNotFound
>>> invalid = Sequence(cat, invalid='PHOTOS')
>>> invalid.next()
Traceback (most recent call last):
...
KeyError
Hierarchical identifiers¶
It is possible to have nested templates (i.e. templates depending on another). This is achieved by placing a placeholder with the name of the parent template and making sure a valid identifier of the parent is passed on each instantiation.
Consider the example of playlists of audio files for each year. We need two template definitions, one for playlists and one for files:
>>> pl = Template.create('PL', '{year}: Playlist {counter}', start=1)
>>> fl = Template.create('FL', '{PL} > Audio File {counter:02d}', start=1)
Let’s get some playlists for different years:
>>> pl15 = Sequence(pl, year=2015)
>>> pl15.next()
'2015: Playlist 1'
>>> pl15.next()
'2015: Playlist 2'
>>> pl16 = Sequence(pl, year=2016)
>>> pl16.next()
'2016: Playlist 1'
Now let’s get some files inside the playlists generated above:
>>> fl15 = Sequence(fl, PL='2015: Playlist 2')
>>> fl15.next()
'2015: Playlist 2 > Audio File 01'
>>> fl15.next()
'2015: Playlist 2 > Audio File 02'
>>> fl16 = Sequence(fl, PL='2016: Playlist 1')
>>> fl16.next()
'2016: Playlist 1 > Audio File 01'
Bulk generations¶
As Sequence is a proper Python iterator, you can use all Python methods that consume them (mainly found on the itertools library). Picking up from the last example with playlists, one might want to generate multiple files for a specific playlist, all at once:
>>> from itertools import islice
>>> list(islice(fl15, 5))
['2016: Playlist 2 > Audio File 03', '2016: Playlist 2 > Audio File 04',
'2016: Playlist 2 > Audio File 05', '2016: Playlist 2 > Audio File 06',
'2016: Playlist 2 > Audio File 07']