Skip to content

Commit

Permalink
Merge af833f5 into 2f27133
Browse files Browse the repository at this point in the history
  • Loading branch information
omelkonian authored Aug 9, 2016
2 parents 2f27133 + af833f5 commit 821b33b
Show file tree
Hide file tree
Showing 10 changed files with 874 additions and 3 deletions.
35 changes: 35 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
..
This file is part of Invenio.
Copyright (C) 2016 CERN.
Invenio is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.

Invenio is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with Invenio; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
MA 02111-1307, USA.

In applying this license, CERN does not
waive the privileges and immunities granted to it by virtue of its status
as an Intergovernmental Organization or submit itself to any jurisdiction.


API Docs
========

.. automodule:: invenio_sequencegenerator.api
:members:

Models
------

.. automodule:: invenio_sequencegenerator.models
:members:
21 changes: 19 additions & 2 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,32 @@
waive the privileges and immunities granted to it by virtue of its status
as an Intergovernmental Organization or submit itself to any jurisdiction.


.. include:: ../README.rst

Installation
User's Guide
------------

This part of the documentation will show you how to get started in using
Invenio-Base.

.. toctree::
:maxdepth: 1
:maxdepth: 2

installation
usage


API Reference
-------------

If you are looking for information on a specific function, class or method,
this part of the documentation is for you.

.. toctree::
:maxdepth: 2

api

Additional Notes
----------------
Expand Down
28 changes: 28 additions & 0 deletions docs/usage.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
..
This file is part of Invenio.
Copyright (C) 2016 CERN.
Invenio is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.

Invenio is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with Invenio; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
MA 02111-1307, USA.

In applying this license, CERN does not
waive the privileges and immunities granted to it by virtue of its status
as an Intergovernmental Organization or submit itself to any jurisdiction.


Usage
=====

.. automodule:: invenio_sequencegenerator
185 changes: 184 additions & 1 deletion invenio_sequencegenerator/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,190 @@
# waive the privileges and immunities granted to it by virtue of its status
# as an Intergovernmental Organization or submit itself to any jurisdiction.

"""Invenio module for generating sequences."""
"""Invenio module for generating sequences.
Invenio-SequenceGenerator is a secondary component of Invenio, responsible for
automatically generating report numbers in a safe manner (i.e. making sure
that every time a client requests a new report number of a specific form, the
server will provide him with a new *unique* report number).
Initialization
==============
First create a Flask application:
>>> from flask import Flask
>>> app = Flask('myapp')
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.init_app(app)
>>> db.create_all()
Usage
=====
Let's see some basic usage examples, such as defining sequences and generating
unique identifiers form them.
Sequence definition
-------------------
The first thing to do is to define your sequence (i.e. the specific form of
your identifiers):
>>> from invenio_sequencegenerator.api import Sequence
>>> seq = Sequence.create('ID', '{counter}-file')
Simple counters
---------------
.. note::
By default, counters start from 0 and increment by 1.
Then you can request new report numbers based on the sequence defined above.
>>> seq.next()
'0-file'
>>> seq.next()
'1-file'
>>> seq.next()
'2-file'
Advanced counters
-----------------
You can also specify the initial counter and increment:
>>> seq = Sequence.create('ID2', '{counter:02d}-file', start=10, step=10)
>>> seq.next()
'10-file'
>>> seq.next()
'20-file'
>>> seq.next()
'30-file'
.. note::
You can format the counter (and all other placeholders) with all the
formatting options provided by Python strings.
User-defined keywords
---------------------
Consider the case where you need to generate identifiers for files of
different categories. Thus, your sequence definition should look like this:
Keyword arguments must be always specified on instantiation.
>>> cat = Sequence.create('KW', '{category}: File {counter:03d}', start=1)
>>> photos = cat.instantiate(category='PHOTOS')
>>> photos.next()
'PHOTOS: File 001'
>>> photos.next()
'PHOTOS: File 002'
>>> videos = cat.instantiate(category='VIDEOS')
>>> videos.next()
'VIDEOS: File 001'
>>> videos.next()
'VIDEOS: File 002'
>>> Sequence.get('invalid')
Traceback (most recent call last):
...
SequenceNotFound
>>> invalid = cat.instantiate(invalid='PHOTOS')
>>> invalid.next()
Traceback (most recent call last):
...
KeyError
Hierarchical report numbers
---------------------------
It is possible to have nested sequences (i.e. sequences depending on another).
This is achieved by placing a placeholder with the name of the parent sequence
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
sequence definitions, one for playlists and one for files. Note that
>>> pl = Sequence.create('PL', '{year}: Playlist {counter}', start=1)
>>> fl = Sequence.create('FL', '{PL} > Audio File {counter:02d}', start=1)
Let's get some playlists for different years.
>>> pl15 = pl.instantiate(year=2015)
>>> pl15.next()
'2015: Playlist 1'
>>> pl15.next()
'2015: Playlist 2'
>>> pl16 = pl.instantiate(year=2016)
>>> pl16.next()
'2016: Playlist 1'
Now let's get some files inside the playlists, generated above.
>>> fl15 = fl.instantiate(PL='2015: Playlist 2')
>>> fl15.next()
'2015: Playlist 2 > Audio File 01'
>>> fl15.next()
'2015: Playlist 2 > Audio File 02'
>>> fl16 = fl.instantiate(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)) # doctest: +SKIP
['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']
Resetting sequences
-------------------
A functionality that might be needed is to reset a specific sequence. This is
achieved via the Sequence.reset method:
>>> seq = Sequence.create('Y', '{year}: {counter}')
>>> y16 = seq.instantiate(year=2016)
>>> y16.next()
'2016: 0'
>>> y16.next()
'2016: 1'
>>> y16.next()
'2016: 2'
>>> y16.reset()
>>> y16.next()
'2016: 0'
>>> y16.reset(start=65)
>>> y16.next()
'2016: 65'
>>> y16.next()
'2016: 66'
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.
- They can contain an optional placeholder for a parent sequence, named as the
parent sequence, defined when the `Sequence.create` API function was called.
- They can contain an arbitrary number of user-defined placeholders, which must
be always passed as keyword arguments on each instantiation.
"""

from __future__ import absolute_import, print_function

Expand Down
81 changes: 81 additions & 0 deletions invenio_sequencegenerator/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# -*- coding: utf-8 -*-
#
# This file is part of Invenio.
# Copyright (C) 2016 CERN.
#
# Invenio is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# Invenio is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Invenio; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.

"""User-level API."""

from __future__ import absolute_import, print_function

from invenio_sequencegenerator.models import SequenceDefinition


class Sequence(object):
"""Iterator for sequences."""

def __init__(self, sequence, **kwargs):
"""Initialise sequence iterator, with or without keyword arguments."""
self.sequence = sequence
self.kwargs = kwargs

@staticmethod
def create(id, template, start=0, step=1):
"""Create a new sequence.
:param id: the identifying name of the sequence
:param template: the template string of the sequence
:param start: the starting counter of the sequence
:param step: the amount to increment at each generation
:return: an iterator of this sequence
"""
return Sequence(SequenceDefinition(id, template, start, step))

@staticmethod
def get(name):
"""Get a new sequence.
:param name: the identifying name of the sequence
:return: an iterator of this sequence
"""
return Sequence(SequenceDefinition.get(name))

def instantiate(self, **kwargs):
"""Fixed keyword arguments for the subsequent calls to `next`.
:param kwargs The formatting arguments of the sequence's template.
"""
return Sequence(self.sequence, **kwargs)

def next(self): # Python 2.x
"""Get next."""
return self.sequence.next(**self.kwargs)

def __next__(self): # Python 3.x
"""Get next."""
return self.next()

def reset(self, start=0):
"""Reset enclosed sequence."""
return self.sequence.reset(start, **self.kwargs)

def erase(self):
"""Erase enclosed sequence."""
self.sequence.erase()

def __iter__(self):
"""Enable iterative capabilities."""
return self
Loading

0 comments on commit 821b33b

Please sign in to comment.