How to create an Invenio overlay¶
TODO
The following items are still missing from this document, feel free to contribute to it.
- celery/redis/honcho/flower
- populate the data
Thanks
What is an overlay¶
Invenio is a library that enable the creation of an digital library but it has to be coupled with an overlay. The overlay will contain your configuration options, your desired look and feel, the extra invenio modules you’ve developed and are using.
Creating your first Invenio overlay¶
If you’ve already setup the developer’s environement invenio itself, it will feel familiar. We are reproducing here the steps for Ubuntu LTS. Other distributions may be found in the previous link.
The global setup¶
Some softwares and libraries are required to work on your overlay. It’s mostly Python, MySQL, Redis as well as XML, XSLT and graphical libraries. Node.js is solely required for development purposes.
$ python --version
Python 2.7.5+
$ sudo apt-get update
$ sudo apt-get install build-essential git redis-server \
libmysqlclient-dev libxml2-dev libxslt-dev \
libjpeg-dev libfreetype6-dev libtiff-dev \
libffi-dev libssl-dev \
software-properties-common python-dev \
virtualenvwrapper
$ sudo pip install -U virtualenvwrapper pip
$ source .bashrc
# Install MySQL server, and keep the root password somewhere safe.
$ sudo apt-get install mysql-server
# Install Node.js
$ sudo add-apt-repository ppa:chris-lea/node.js
$ sudo apt-get update
$ sudo apt-get install nodejs
The virtual environment¶
Python development usually recommends to work within a virtualenv
, which
creates an isolated environment where the libraries required by one will not
intervene with the system ones or the ones of another system. We are using
virtualenvwrapper
but nothing prevents your from directly using
virtualenv
you are familiar with it.
$ mkvirtualenv myoverlay
(myoverlay)$ # we are in your overlay environment now and
(myoverlay)$ # can leave it using the deactivate command.
(myoverlay)$ deactivate
$ # Now join it back, recreating it would fail.
$ workon myoverlay
(myoverlay)$ # That's all there is to know about it.
The base of the overlay¶
Let’s dive in.
$ workon myoverlay
(myoverlay)$ cdvirtualenv
(myoverlay)$ mkdir -p src/myoverlay
(myoverlay)$ cd src/myoverlay
(myoverlay)$ edit setup.py
The setup.py
file contains the definition of a python package. Having one
means you can rely on existing tools like pip
to package, install, deploy
it later on. Here is its minimal content.
from setuptools import setup
from setuptools import setup, find_packages
packages = find_packages()
setup(
name="My Overlay",
version="0.1.dev0",
url="http://invenio-software.org/",
author="Invenio Software",
author_email="invenio@invenio-software.org",
description="My first overlay",
packages=packages,
install_requires=[
"Invenio>=2"
],
entry_points={
"invenio.config": ["myoverlay = myoverlay.config"]
}
)
Now we can install it in editable mode (-e
), meaning you don’t have to
reinstall it after each change
(myoverlay)$ pip install -e .
This will fetch the latest Invenio version published on PyPI. As a developer,
you may instead want to use the development version of Invenio from GitHub. To
do so, create a file called requirements.txt
with the following content:
git+git://github.com/inveniosoftware/invenio@pu#egg=Invenio-dev
-e .
and install using:
(myoverlay)$ pip install -r requirements.txt
Configuration¶
As you’ve seen above, we defined an entry_point for myoverlay.config
. It
points to a module that will contain our configuration. So create your
application.
src/
│
├ myoverlay/
│ │
│ ├ base/
│ │ │
│ │ └ __init__.py
│ │
│ ├ __init__.py
│ └ config.py
│
├ requirements.txt
└ setup.py
Put the required configuration into config.py
.
CFG_SITE_LANGS = ["en"]
CFG_SITE_NAME = "My Overlay"
CFG_SITE_NAME_INTL = {
"en": CFG_SITE_NAME
}
PACKAGES = [
"myoverlay.base",
"invenio.modules.*",
"invenio.base",
]
try:
from myoverlay.instance_config import *
except ImportError:
pass
Sensitive configuration¶
Other configuration elements like database username and password or the website url should not be put here as this file is not specific to the installation and may be put under a version control system such as Git or Subversion.
The configuration can be handled via the inveniomanage command line interface (or by editing the invenio.cfg file in the instance folder and reloading the application).
(myoverlay)$ inveniomanage config set create secret-key
# MySQL configuration
(myoverlay)$ inveniomanage config set CFG_DATABASE_NAME mysql-database
(myoverlay)$ inveniomanage config set CFG_DATABASE_USER mysql-user
# HOST configuration (for redirects, etc.)
(myoverlay)$ inveniomanage config set CFG_SITE_URL http://invenio.example.com
(myoverlay)$ inveniomanage config set CFG_SITE_SECURE_URL https://invenio.example.com
(myoverlay)$ inveniomanage config set DEBUG True
(myoverlay)$ inveniomanage config set ASSETS_DEBUG True
Database setup¶
(invenio)$ inveniomanage database init --user=root --password=$MYSQL_ROOT --yes-i-know
...
>>> Database has been installed.
(invenio)$ inveniomanage database create
...
>>> Tables filled successfully.
Assets¶
Most of the JavaScript and CSS libraries used are not bundled with invenio itself and needs to be downloaded via bower. Bower is configured using two files:
- .bowerrc: tells where the assets are downloaded
- bower.json: lists the dependencies to be downloaded
{
"directory": "myoverlay/base/static/vendors"
}
The bower.json
can be automagically generated.
$ sudo su -c "npm install -g bower less clean-css requirejs uglify-js"
(myoverlay)$ inveniomanage bower > bower.json
(myoverlay)$ bower install
For invenio to see the static files from the myoverlay.base
module, it
needs to declare a Flask blueprint. Create the following file:
myoverlay/base/views.py
.
from flask import Blueprint
blueprint = Blueprint(
"myoverlay",
__name__,
url_prefix="/",
template_folder="templates", # where your custom templates will go
static_folder="static" # where the assets go
)
The assets will now be collected into the instance static folder from your overlay, invenio itself and every libraries it uses.
(myoverlay)$ inveniomanage collect
Running¶
(myoverlay)$ inveniomanage runserver
Translations¶
Invenio comes with full internationalization and localization support
based on Babel library and Flask-Babel. All strings you want to
translate in your overlay have to be marked with _()
.
When you have all strings properly marked, it is time to prepare catalog that contains all these strings for tranlations to desired languages.
Configuration¶
First of all, you have to get into the source folder of your overlay and create a configuration file for Babel.
[python: **.py]
encoding = utf-8
[jinja2: **/templates/**]
encoding = utf-8
extensions = jinja2.ext.autoescape.jinja2.ext.with_
Save it as babel.cfg
next to your setup.py
. Before we run the
extraction tool we need to add section to configure translation directory
to setup.cfg
.
[compile_catalog]
directory = myoverlay/base/translations/
[extract_messages]
output-file = myoverlay/base/translations/myoverlay.pot
[init_catalog]
input-file = myoverlay/base/translations/myoverlay.pot
output-dir = myoverlay/base/translations/
[update_catalog]
input-file = myoverlay/base/translations/myoverlay.pot
output-dir = myoverlay/base/translations/
Message Extraction¶
Then it’s time to run the Babel string extraction with given configuration:
(myoverlay)$ python setup.py extract_messages
Create Catalog for New Language¶
Once all translatable strings are extracted, one need to prepare catalogs for new languages. Following example shows how to prepare new catalog for French in PO (Portable Object) format.
(myoverlay)$ python setup.py init_catalog -l fr
Now edit the myoverlay/base/translations/fr/LC_MESSAGES/messages.po
file as needed.
Compiling Catalog¶
Next step is to prepare MO (Machine Object) files in the format which is defined by the GNU gettext tools and the GNU translation project.
To compile the translations for use, pybabel integration with distutils helps again:
(myoverlay)$ python setup.py compile_catalog
If you install Invenio in development mode you must compile catalog also from the Invenio directory project.
Note
You should tell git to ignore your compliled translation by running:
$ echo \*.mo >> .gitignore
Updating Strings¶
It is pretty common that your strings in the code will change over the
time. Pybabel provides support for updating the translation catalog with
new strings or changing existing ones. What do you have to do? Create a
new myoverlay.pot
like above and then let pybabel merge the changes:
$ python setup.py update_catalog
Deployment¶
Deploying Invenio is almost a piece of cake using Fabric. The following step are inspired by the Flask documentation: Deploying with Fabric
Prerequisites¶
First, you need a server with remote access (SSH), where you’ve installed all
the python dependencies (e.g. build-essentials
, python-dev
,
libmysqlclient-dev
, etc.).
Install fabric locally,
$ pip install fabric
and create a boilerplate fabfile.py
:
import json
from fabric.api import *
from fabric.utils import error
from fabric.contrib.files import exists
env.user = 'invenio' # remote username
env.directory = '/home/invenio/www' # remote directory
env.hosts = ['yourserver'] # list of servers
Preparing the tarball¶
Before deploying anything, we need to locally prepare the python package to be
installed. Thanks to our setup.py
file, it’s very simple.
Beforehand, we have to generate the static assets into our static folder. By
doing so, it’s not required to install anything related to node.js on your
server (no bower
, less
, uglifyjs
, etc.).
@task
def pack():
"""Create a new source distribution as tarball."""
with open(".bowerrc") as fp:
bower = json.load(fp)
local("inveniomanage assets build --directory {directory}/gen"
.format(**bower))
return local("python setup.py sdist --formats=gztar", capture=False) \
.succeeded
Try it:
$ fab pack
...
Done
$ ls dist/
My-Overlay-0.1.dev0.tar.gz
This is the package that will be installed on your server.
Creating the virtual environement¶
We love virtual environments. We recommend you to install each version into its own virtual env enabling quick rollbacks.
@task
def create_virtualenv():
"""Create the virtualenv."""
package = local("python setup.py --fullname", capture=True).strip()
venv = "{0}/{1}".format(env.directory, package)
with cd(env.directory):
if exists(package):
return error("This version {0} is already installed."
.format(package))
return run("virtualenv {0}".format(package)).succeeded
Installing the package¶
We can now upload the local tarball into the virtualenv, and install everything there.
@task
def install():
"""Install package."""
package = local("python setup.py --fullname", capture=True).strip()
venv = "{0}/{1}".format(env.directory, package)
if not exists(venv):
return error("Meh? I need a virtualenv first.")
# Upload the package and put it into our virtualenv.
put("dist/{0}.tar.gz".format(package), "/tmp/app.tgz")
run("mkdir -p {0}/src".format(venv))
with cd("{0}/src".format(venv)):
run("tar xzf /tmp/app.tgz")
run("rm -rf /tmp/app.tgz")
# Jump into the virtualenv and install stuff
with cd("{0}/src/{1}".format(venv, package)):
success = run("{0}/bin/python setup.py install".format(venv)
if success:
# post install
run("{0}/bin/inveniomanage collect".format(venv))
return success
Combining all the three steps:
$ fab pack virtualenv install
Configuration¶
The setup doesn’t have the invenio.cfg
file that is generated via
inveniomanage config
. You should do so manually.
Running the server¶
uWSGI is super simple and neat, all you need is two files. In the example below, we’ve installed two versions of our overlay and a symbolic link is pointing to the one we want to run.
$ ls www/
current -> My-Overlay-0.1
My-Overlay-0.1.dev1
My-Overlay-0.1.dev2
My-Overlay-0.1
wsgi.py
uwsgi.ini
Let’s create the wsgi.py
file.
from invenio.base.factory import create_wsgi_app
application = create_wsgi_app()
And the µWSGI configuration:
[uwsgi]
http = 0.0.0.0:4000
master = true
processes = 4
die-on-term = true
vaccum = true
chdir = %d
virtualenv = %d/current/
module = wsgi:application
touch-reload = %d/wsgi.py
Let’s run it.
$ pip install uwsgi
$ uwsgi --ini uwsgi.ini
# or in daemon mode
$ uwsgi -d uwsgi.log --ini uwsgi.ini
If the new version causes troubles, going back to the old one is as fast as changing the symbolic link and restarting the WSGI server.
$ rm current
$ ln -s My-Overlay-0.1.dev1 current
$ touch wsgi.py
Dealing with versions¶
One good idea is to use symlink to point to your current virtualenv and run your overlay from there. Doing that via Fabric is left as an exercise to the reader.
When installing a new version, copying the invenio.cfg
file over is the
only requirements. Restarting the WSGI server is usually done by touch
-ing
the wsgi.py
file.