This document contains instructions for Studio developers.
It is first required to install the system dependencies. Look at the installation guide for that.
It is highly recommended to install Studio in a virtual Python environment, so you’ll start with that.
Unix users will use this:
$ wget http://pypi.python.org/packages/source/v/virtualenv/virtualenv-1.4.8.tar.gz
$ tar xvzf virtualenv-1.4.8.tar.gz
$ cp virtualenv-1.4.8/virtualenv.py ./
$ rm -rf virtualenv-1.4.8
$ rm virtualenv-1.4.8.tar.gz
Windows users will download the file through their browsers and use a tool such as 7-zip from http://www.7-zip.org to extract the files.
You are now ready to create a virtual Python environment. Here’s how it looks like on Windows:
C:\>C:\Python25\python.exe "C:\Documents and Settings\Administrator\virtualenv.py" C:\env
New python executable in C:\env\Scripts\python.exe
Installing setuptools..............done.
Here’s how it looks like on Unix:
$ python virtualenv.py /path/to/new/virtual/env
You can create your virtual environment wherever you want.
Download Studio from gitorious:
$ git clone http://git.gitorious.org/erilem/studio.git
Now that you have a virtual Python environment ready, you can proceed with the installation of Studio.
On Linux:
$ source /path/to/virtual/env/bin/activate
$ cd /path/to/Studio
$ python setup.py develop
$ python setup.py compile_catalog
This will install Studio and its dependencies, then compile the localization files for i18n.
Windows users need to adapt the above commands.
To build the documentation, a Sphinx egg must first be installed in the virtual Python environment:
$ easy_install "Sphinx==1.0.7"
Buiding the doc is done as follows:
$ cd /path/to/Studio/docs
$ make html
This creates the HTML documentation in the /path/to/Studio/docs/.build directory, index.html being the entry point.
Sphinx documentation: http://sphinx.pocoo.org/contents.html
To run the unit and functional tests run the nosetests command at the root of the project directory (the Studio directory):
$ nosetests -d
The nosetests documentation gives this for the -d switch:
Add detail to error output by attempting to evaluate
failed asserts [NOSE_DETAILED_ERRORS]
The authentication and authorization system is based on the repoze middleware (repoze.who and repose.what).
Authentication is based on the following database tables :
The authorization system will then allow a response based on group membership or permission check. In practical terms, you need to populate the database with some groups and permissions, then link each group with a set of permissions.
Once this is done, you will be able to allow or deny every action of controllers, using the @ActionProtector decorator and using built-in or custom predicates. You will find some good documentation on predicates, and a list of built-in predicates here:
So let’s go with a simple example. Imagine we want to deny access to action delete of our controller UsersController to all users except those who have the permission ‘delete users’. We simply need to add the decorator @ActionProtector with the build-in predicate has_permission('delete users') to our delete action:
from repoze.what.predicates import has_permission
from repoze.what.plugins.pylonshq import ActionProtector
class UsersController(BaseController):
@ActionProtector(has_permission('delete users'))
def delete(self):
# [ ... ]
return 'User deleted'
If the predicate has_permission('delete users') is not fulfilled, a 401 or 403 error will be returned.
You may also need to know the identity of the user logged in. You can achieve this by looking at repoze.what.credentials environnement variable. So the user login is accessible via:
request.environ.get('repoze.what.credentials')['repoze.what.userid']
This section describes the HTTP Interfaces the server side of Studio exposes.
request: GET /datastores
response: a JSON document of this form:
{
"datastores": [
{
"text": "{datastore_name}",
"href": "{datastore_href}",
"type": "{datastore_type}",
"id": {datastore_id}
},
...
]
}
success status code: 200 OK
manual test:
curl -X GET http://localhost:5000/datastores
request: GET /datastores/{datastore_id}
response: a JSON document of this form
{
"datasources": [
{
"text": "{datasource_name}",
"leaf": [true|false],
"type": "[RASTER|POINT|LINE|POLYGON]",
"id": "{datasource_id}"
},
...
],
"type": "[directory|postgis]",
"id": {directory_id},
"name": "{directory_name}"
}
success status code: 200 OK
notes:
manual test:
curl -X GET http://localhost:5000/datastores/1
request: GET /datastores/{datastore_id}/datasources
response: a JSON document of this form
{
"datasources": [
{
"href": "{datasource_href}",
"text": "{datasource_name}",
"leaf": true,
"type": "[RASTER|POINT|LINE|POLYGON]",
"id": "{datasource_id}"
},
...
]
}
success status code: 200 OK
notes:
manual test:
curl -X GET http://localhost:5000/datastores/1/datasources
request: GET /datastores/{datastore_id}/datasources/{datasource_id}/mapfile
response: a JSON document representing the mapfile section for this datasource
manual test:
curl -X GET http://localhost:5000/datastores/1/datasources/3dfa880a8e37bcc97ff8bbeb9aff7852/mapfile
request: GET /datastores/{datastore_id}/datasources/{datasource_id}/mapfile[?{classificationparams}]
the classifications params:
ramp: interpolate between a start and end color
qualitative: use a predefined palette
examples:
curl -X GET http://localhost:5000/datastores/1/datasources/3dfa880a8e37bcc97ff8bbeb9aff7852/mapfile?classification=quantile&startcolor=ff0000&endcolor=0000ff&attribute=POP2005&intervals=5
curl -X GET http://localhost:5000/datastores/1/datasources/3dfa880a8e37bcc97ff8bbeb9aff7852/mapfile?classification=quantile&colortype=ramp&interpolation=HSV&startcolor=ff2200&endcolor=0022ff&attribute=POP2005&intervals=7
curl -X GET http://localhost:5000/datastores/1/datasources/3dfa880a8e37bcc97ff8bbeb9aff7852/mapfile?classification=quantile&colortype=qualitative&theme=0&attribute=POP2005&intervals=5
curl -x GET http://localhost:5000/datastores/1/datasources/3dfa880a8e37bcc97ff8bbeb9aff7852/mapfile?classification=unique&colortype=qualitative&theme=0&attribute=REGION
response: a JSON document representing a default mapfile layer excerpt for this datasource
success status code: 200 OK
request: GET /datastores/{datastore_id}/datasources/{datasource_id}/columns
success status code: 200 OK
response body:
{ "columns": [
{
"name":"{colum_name}",
"type":"[string|numeric]"
},
...
]}
manual test:
curl -X GET http://localhost:5000/datastores/1/datasources/3dfa880a8e37bcc97ff8bbeb9aff7852/columns
request: GET /mapfiles
response: a JSON document of this form:
{ "maps": [
{
"id": "{mapfile_id}",
"href": "{mapfile_href}",
"name": "{mapfile_name}"
},
...
]}
success status code: 200 OK
request: GET /mapfiles/{mapfile_id}/symbols
response: a JSON document representing symbols of a mapfile:
{ "symbols": [
{
"id": 1,
"name": "dash"
},
{
"id": 2,
"name": "parking"
},
...
]}
success status code: 200 OK
request: GET /mapfiles/{mapfile_id}/fonts
response: a JSON document representing fonts of a mapfile:
{ "fonts": [
{
"id": "verdana",
"name": "verdana"
},
{
"id": "arial",
"name": "arial"
},
...
]}
success status code: 200 OK
request: POST /mapfiles
request body: a JSON document representing a mapfile
success status code: 201 CREATED
response: a JSON document of this form:
{
"id": "{mapfile_id}",
"href": "{mapfile_href}",
"name": "{mapfile_name}"
}
request: GET /layertemplates
response: a JSON document of this form:
{ "layertemplates": [
{
"id": "{layertemplate_id}",
"href": "{layertemplate_href}",
"name": "{layertemplate_name}",
"comment": "{layertemplate_comment}"
},
...
]}
success status code: 200 OK
request: GET /layertemplates/{layertemplate_id}
response: a JSON document of this form:
{
"id": "{layertemplate_id}",
"name": "{layertemplate_name}",
"comment": "{layertemplate_comment}",
"user_id": "{layertemplate_user_id}",
"json": {
// the json representing the layer template
}
}
success status code: 200 OK
request: POST /layertemplates
request body: a JSON document of this form:
{
"name": "{layertemplate_name}",
"comment": "{layertemplate_comment}",
"json": {
// the json representing the layer template
}
}
success status code: 201 CREATED
response: a JSON document of this form:
{
"id": "{layertemplate_id}",
"href": "{layertemplate_href}",
"name": "{layertemplate_name}",
"comment": "{layertemplate_comment}"
}
request: PUT /layertemplates/{layertemplate_id}
request body: a JSON document of this form:
{
"name": "{layertemplate_name}",
"comment": "{layertemplate_comment}",
"json": {
// the json representing the layer template
}
}
success status code: 201 CREATED