Add sqlite backend

This commit is contained in:
Cédric Krier committed 2009-07-07 19:35:44 +02:00
1 parent af9ad2664b
commit 23c5eff184
9 files changed
+956 -3

No files matched your search

+5 -1
View File
@@ -21,6 +21,10 @@
# Configure the port for the webdav protocol to listen
#webdavport = 8080
# Configure the database type
# allowed values are postgresql, sqlite
db_type = postgresql
# Configure the database connection
## Note: Only databases owned by db_user will be displayed in the connection dialog
## of the Tryton client. db_user must have create permission for new databases
@@ -54,7 +58,7 @@
#smtp_password = False
#smtp_user = False
# Configure the path to store attachments
# Configure the path to store attachments and sqlite database
#data_path = /var/lib/trytond
# Limit the number of threads
+1 -1
View File
@@ -41,12 +41,12 @@ setup(name=PACKAGE,
],
license=LICENSE,
install_requires=[
'psycopg2 >= 2.0',
'lxml',
'egenix-mx-base',
'relatorio >= 0.2.0',
],
extras_require={
'PostgreSQL': ['psycopg2 >= 2.0'],
'WebDAV': ['PyWebDAV >= 0.9.3'],
'PDF': ['openoffice.interact'],
'SSL': ['pyOpenSSL'],
+5 -1
View File
@@ -1,4 +1,8 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
from postgresql import *
from trytond.config import CONFIG
if CONFIG['db_type'] == 'sqlite':
from sqlite import *
else:
from postgresql import *
+6
View File
@@ -0,0 +1,6 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
from database import *
from fields import *
from table import *
+297
View File
@@ -0,0 +1,297 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
from trytond.backend.database import DatabaseInterface, CursorInterface
from trytond.config import CONFIG
from trytond.session import Session
import logging
import os
import re
import mx.DateTime
from decimal import Decimal
try:
from pysqlite2 import dbapi2 as sqlite
from pysqlite2.dbapi2 import IntegrityError as DatabaseIntegrityError
from pysqlite2.dbapi2 import OperationalError as DatabaseOperationalError
except ImportError:
import sqlite3 as sqlite
from sqlite3 import IntegrityError as DatabaseIntegrityError
from sqlite3 import OperationalError as DatabaseOperationalError
def date_trunc(_type, date):
if _type == 'second':
return date
try:
date = mx.DateTime.strptime(date, '%Y-%m-%d %H:%M:%S')
except:
return None
if _type == 'year':
return "%i-01-01 00:00:00" % dt.year
elif _type == 'month':
return "%i-%02i-01 00:00:00" % (dt.year, dt.month)
elif _type == 'day':
return "%i-%02i-%02i 00:00:00" % (dt.year, dt.month, dt.day)
def now():
return mx.DateTime.now().strftime('%Y-%m-%d %H:%M:%S')
class Database(DatabaseInterface):
_memory_database = None
_memory_created = False
_conn = None
def __new__(cls, database_name=':memory:'):
if database_name in (':memory:', 'memory') \
and cls._memory_database:
return cls._memory_database
return DatabaseInterface.__new__(cls, database_name=database_name)
def __init__(self, database_name=':memory:'):
super(Database, self).__init__(database_name=database_name)
if database_name in (':memory:', 'memory'):
Database._memory_database = self
def connect(self):
if self._conn is not None:
return self
if self.database_name in (':memory:', 'memory'):
path = ':memory:'
if self.database_name == 'memory' \
and not self._memory_created:
raise Exception('Database doesn\'t exist!')
else:
path = os.path.join(CONFIG['data_path'],
self.database_name + '.sqlite')
if not os.path.isfile(path):
raise Exception('Database doesn\'t exist!')
self._conn = sqlite.connect(path, detect_types=sqlite.PARSE_DECLTYPES)
self._conn.create_function('date_trunc', 2, date_trunc)
self._conn.create_function('now', 0, now)
return self
def cursor(self, autocommit=False):
if self._conn is None:
self.connect()
if autocommit:
self._conn.isolation_level = None
else:
self._conn.isolation_level = 'IMMEDIATE'
return Cursor(self._conn, self.database_name)
def close(self):
if self.database_name in (':memory:', 'memory'):
return
if self._conn is None:
return
self._conn = None
def create(self, cursor, database_name):
if database_name in (':memory:', 'memory'):
path = ':memory:'
if database_name == 'memory':
self._memory_created = True
else:
if os.sep in database_name:
return
path = os.path.join(CONFIG['data_path'],
database_name + '.sqlite')
conn = sqlite.connect(path)
cursor = conn.cursor()
cursor.close()
conn.close()
def drop(self, cursor, database_name):
if database_name in (':memory', 'memory'):
self._conn = None
return
if os.sep in database_name:
return
os.remove(os.path.join(CONFIG['data_path'],
database_name + '.sqlite'))
@staticmethod
def dump(database_name):
if database_name in (':memory:', 'memory'):
raise Exception('Unable to dump memory database!')
if os.sep in database_name:
raise Exception('Wrong database name!')
path = os.path.join(CONFIG['data_path'],
database_name + '.sqlite')
file_p = file(path)
data = file_p.read()
file_p.close()
return data
@staticmethod
def restore(database_name, data):
if database_name in (':memory:', 'memory'):
raise Exception('Unable to restore memory database!')
if os.sep in database_name:
raise Exception('Wrong database name!')
path = os.path.join(CONFIG['data_path'],
database_name + '.sqlite')
if os.path.isfile(path):
raise Exception('Database already exists!')
file_p = file(path, 'wb')
file_p.write(data)
file_p.close()
@staticmethod
def list(cursor):
res = []
for file in os.listdir(CONFIG['data_path']) + ['memory']:
if file.endswith('.sqlite') or file == 'memory':
if file == 'memory':
db_name = ':memory:'
else:
db_name = file[:-7]
try:
database = Database(db_name)
except:
continue
cursor2 = database.cursor()
if cursor2.test():
res.append(db_name)
cursor2.close()
return res
@staticmethod
def init(cursor):
sql_file = os.path.join(os.path.dirname(__file__), 'init.sql')
for line in file(sql_file).read().split(';'):
if (len(line)>0) and (not line.isspace()):
cursor.execute(line)
for i in ('ir', 'workflow', 'res', 'webdav'):
root_path = os.path.join(os.path.dirname(__file__), '..', '..')
tryton_file = os.path.join(root_path, i, '__tryton__.py')
mod_path = os.path.join(root_path, i)
info = eval(file(tryton_file).read())
active = info.get('active', False)
if active:
state = 'to install'
else:
state = 'uninstalled'
cursor.execute('INSERT INTO ir_module_module ' \
'(author, website, name, shortdesc, ' \
'description, state) ' \
'VALUES (%s, %s, %s, %s, %s, %s)',
(info.get('author', ''),
info.get('website', ''), i, info.get('name', False),
info.get('description', ''), state))
cursor.execute('SELECT last_insert_rowid()')
module_id = cursor.fetchone()[0]
dependencies = info.get('depends', [])
for dependency in dependencies:
cursor.execute('INSERT INTO ir_module_module_dependency ' \
'(module, name) VALUES (%s, %s)',
(module_id, dependency))
class _Cursor(sqlite.Cursor):
def __build_dict(self, row):
res = {}
for i in range(len(self.description)):
res[self.description[i][0]] = row[i]
return res
def dictfetchone(self):
row = self.fetchone()
if row:
return self.__build_dict(row)
else:
return row
def dictfetchmany(self, size):
res = []
rows = self.fetchmany(size)
for row in rows:
res.append(self.__build_dict(row))
return res
def dictfetchall(self):
res = []
rows = self.fetchall()
for row in rows:
res.append(self.__build_dict(row))
return res
class Cursor(CursorInterface):
IN_MAX = 500
def __init__(self, conn, database_name):
self._conn = conn
self.database_name = database_name
self.dbname = self.database_name #XXX to remove
self.cursor = conn.cursor(_Cursor)
def __getattr__(self, name):
return getattr(self.cursor, name)
def execute(self, sql, params=None):
sql = sql.replace('?', '??')
quote_separation = re.compile(r"(.*?)('.*?')", re.DOTALL)
notQuoted_quoted = quote_separation.findall(sql+"''")
replaced = [nq\
.replace('%s', '?')\
.replace('ilike', 'like') \
+ q for (nq, q) in notQuoted_quoted]
sql = "".join(replaced)[:-2]
try:
if params:
res = self.cursor.execute(sql, params)
else:
res = self.cursor.execute(sql)
except:
print sql, params
raise
return res
def close(self, close=False):
self.cursor.close()
self.rollback()
def commit(self):
self._conn.commit()
def rollback(self):
self._conn.rollback()
def test(self):
self.cursor.execute("SELECT name " \
"FROM sqlite_master " \
"WHERE type = 'table' AND name in (" \
"'ir_model', "
"'ir_model_field', "
"'ir_ui_view', "
"'ir_ui_menu', "
"'res_user', "
"'res_group', "
"'wkf', "
"'wkf_activity', "
"'wkf_transition', "
"'wkf_instance', "
"'wkf_workitem', "
"'wkf_witm_trans', "
"'ir_module_module', "
"'ir_module_module_dependency, '"
"'ir_translation, '"
"'ir_lang'"
")")
return len(self.cursor.fetchall()) != 0
def lastid(self):
self.cursor.execute('SELECT last_insert_rowid()')
return self.cursor.fetchone()[0]
def has_lock(self):
return False
sqlite.register_converter('NUMERIC', lambda val: Decimal(str(val)))
sqlite.register_adapter(Decimal, lambda val: float(val))
sqlite.register_adapter(Session, lambda val: int(val))
+196
View File
@@ -0,0 +1,196 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
try:
import hashlib
except ImportError:
hashlib = None
import sha
class Field(object):
@staticmethod
def sql_format(value):
if value is None or value == False:
return None
elif isinstance(value, str):
return unicode(value, 'utf-8')
elif isinstance(value, unicode):
return value
return unicode(value)
@staticmethod
def sql_type(field):
return None
class Boolean(Field):
@staticmethod
def sql_format(value):
return value and '1' or '0'
@staticmethod
def sql_type(field):
return ('BOOLEAN', 'BOOLEAN')
class Integer(Field):
@staticmethod
def sql_format(value):
return int(value or 0)
@staticmethod
def sql_type(field):
return ('INTEGER', 'INTEGER')
class BigInteger(Integer):
@staticmethod
def sql_type(field):
return ('INTEGER', 'INTEGER')
class Char(Field):
@staticmethod
def sql_type(field):
return ('VARCHAR', 'VARCHAR')
class Sha(Field):
@staticmethod
def sql_format(value):
if hashlib:
return value and hashlib.sha1(value).hexdigest() or ''
else:
return value and sha.new(value).hexdigest() or ''
@staticmethod
def sql_type(field):
return ('VARCHAR', 'VARCHAR(40)')
class Text(Field):
@staticmethod
def sql_type(field):
return ('TEXT', 'TEXT')
class Float(Field):
@staticmethod
def sql_format(value):
return float(value or 0.0)
@staticmethod
def sql_type(field):
return ('FLOAT', 'FLOAT')
class Numeric(Float):
@staticmethod
def sql_type(field):
return ('NUMERIC', 'NUMERIC')
class Date(Field):
@staticmethod
def sql_type(field):
return ('DATE', 'DATE')
class DateTime(Field):
@staticmethod
def sql_type(field):
return ('TIMESTAMP', 'TIMESTAMP')
class Time(Field):
@staticmethod
def sql_type(field):
return ('TIME', 'TIME')
class Binary(Field):
@staticmethod
def sql_format(value):
return value or None
@staticmethod
def sql_type(field):
return ('BLOB', 'BLOB')
class Selection(Char):
@staticmethod
def sql_type(field):
return ('VARCHAR', 'VARCHAR')
class Reference(Field):
@staticmethod
def sql_type(field):
return ('VARCHAR', 'VARCHAR')
class Many2One(Field):
@staticmethod
def sql_format(value):
return value and int(value) or None
@staticmethod
def sql_type(field):
return ('INTEGER', 'INTEGER')
class One2Many(Field):
pass
class Many2Many(Field):
pass
class Function(Field):
pass
class Property(Function):
pass
FIELDS = {
'boolean': Boolean,
'integer': Integer,
'biginteger': BigInteger,
'char': Char,
'sha': Sha,
'text': Text,
'float': Float,
'numeric': Numeric,
'date': Date,
'datetime': DateTime,
'time': Time,
'binary': Binary,
'selection': Selection,
'reference': Reference,
'many2one': Many2One,
'one2many': One2Many,
'many2many': Many2Many,
'function': Function,
'property': Property,
}
+194
View File
@@ -0,0 +1,194 @@
-------------------------------------------------------------------------
-- Modules Description
-------------------------------------------------------------------------
CREATE TABLE ir_model (
id INTEGER PRIMARY KEY,
model VARCHAR,
name VARCHAR,
info TEXT,
module VARCHAR
);
CREATE TABLE ir_model_field (
id INTEGER PRIMARY KEY,
model INTEGER,
name VARCHAR,
relation VARCHAR,
field_description VARCHAR,
ttype VARCHAR,
help TEXT,
module VARCHAR
);
-------------------------------------------------------------------------
-- Actions
-------------------------------------------------------------------------
CREATE TABLE ir_ui_view (
id INTEGER PRIMARY KEY,
model VARCHAR,
"type" VARCHAR,
arch TEXT,
field_childs VARCHAR,
priority INTEGER
);
CREATE TABLE ir_ui_menu (
id INTEGER PRIMARY KEY,
parent_id INTEGER,
name VARCHAR,
icon VARCHAR
);
--------------------------------
-- Translation
--------------------------------
CREATE TABLE ir_translation (
id INTEGER PRIMARY KEY,
lang VARCHAR,
src TEXT,
name VARCHAR,
res_id INTEGER,
value TEXT,
"type" VARCHAR,
module VARCHAR,
fuzzy BOOLEAN
);
CREATE TABLE ir_lang (
id INTEGER PRIMARY KEY,
name VARCHAR,
code VARCHAR,
translatable BOOLEAN,
active BOOLEAN,
direction VARCHAR
);
---------------------------------
-- Res user
---------------------------------
-- level:
-- 0 RESTRICT TO USER
-- 1 RESTRICT TO GROUP
-- 2 PUBLIC
CREATE TABLE res_user (
id INTEGER PRIMARY KEY,
name VARCHAR,
active BOOLEAN,
login VARCHAR,
password VARCHAR(40),
-- action_id INTEGER references ir_act_window on delete set null,
action INTEGER
);
insert into res_user (id,login,password,name,action,active) values (0,'root',NULL,'Root',NULL,0);
CREATE TABLE res_group (
id INTEGER PRIMARY KEY,
name VARCHAR NOT NULL
);
CREATE TABLE "res_user-res_group" (
id INTEGER PRIMARY KEY,
uid INTEGER,
gid INTEGER
);
---------------------------------
-- Workflows
---------------------------------
create table wkf
(
id INTEGER PRIMARY KEY,
name VARCHAR,
osv VARCHAR,
on_create BOOLEAN
);
create table wkf_activity
(
id INTEGER PRIMARY KEY,
workflow INTEGER,
subflow INTEGER,
split_mode VARCHAR,
join_mode VARCHAR,
kind VARCHAR,
name VARCHAR,
signal_send VARCHAR,
flow_start BOOLEAN,
flow_stop BOOLEAN,
action TEXT
);
create table wkf_transition
(
id INTEGER PRIMARY KEY,
act_from INTEGER,
act_to INTEGER,
condition VARCHAR,
trigger_expr_id VARCHAR,
signal VARCHAR,
"group" INTEGER
);
create table wkf_instance
(
id INTEGER PRIMARY KEY,
workflow INTEGER,
uid INTEGER,
res_id INTEGER not null,
res_type VARCHAR not null,
state VARCHAR not null
);
create table wkf_workitem
(
id INTEGER PRIMARY KEY,
activity INTEGER,
instance INTEGER,
subflow INTEGER,
state VARCHAR
);
create table wkf_witm_trans
(
id INTEGER PRIMARY KEY,
trans_id INTEGER,
inst_id INTEGER
);
---------------------------------
-- Modules
---------------------------------
CREATE TABLE ir_module_module (
id INTEGER PRIMARY KEY,
create_uid INTEGER,
create_date TIMESTAMP,
write_date TIMESTAMP,
write_uid INTEGER,
website VARCHAR,
name VARCHAR,
author VARCHAR,
url VARCHAR,
state VARCHAR,
shortdesc VARCHAR,
description TEXT
);
CREATE TABLE ir_module_module_dependency (
id INTEGER PRIMARY KEY,
create_uid INTEGER,
create_date TIMESTAMP,
write_date TIMESTAMP,
write_uid INTEGER,
name VARCHAR,
module INTEGER
);
+251
View File
@@ -0,0 +1,251 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
from trytond.backend.table import TableHandlerInterface
from trytond.backend.sqlite.fields import FIELDS
import logging
import re
class TableHandler(TableHandlerInterface):
def __init__(self, cursor, model, module_name=None, history=False):
super(TableHandler, self).__init__(cursor, model,
module_name=module_name, history=history)
self._columns = {}
self._constraints = []
self._fk_deltypes = {}
self._indexes = []
self._field2module = {}
self._model = model
# Create new table if necessary
if not self.table_exist(self.cursor, self.table_name):
if not self.history:
self.cursor.execute('CREATE TABLE "%s" ' \
'(id INTEGER PRIMARY KEY)' % \
self.table_name)
else:
self.cursor.execute('CREATE TABLE "%s" ' \
'(__id INTEGER PRIMARY KEY, ' \
'id INTEGER)' % self.table_name)
self._update_definitions()
@staticmethod
def table_exist(cursor, table_name):
cursor.execute("SELECT name FROM sqlite_master " \
"WHERE type = 'table' AND name = %s",
(table_name,))
return bool(cursor.fetchone())
@staticmethod
def table_rename(cursor, old_name, new_name):
if TableHandler.table_exist(cursor, old_name) and \
not TableHandler.table_exist(cursor, new_name):
cursor.execute('ALTER TABLE "%s" RENAME TO "%s"' % \
(old_name, new_name))
@staticmethod
def sequence_exist(cursor, sequence_name):
return True
@staticmethod
def sequence_rename(cursor, old_name, new_name):
pass
def column_exist(self, column_name):
return column_name in self._columns
def _update_definitions(self):
# Fetch columns definitions from the table
self.cursor.execute('PRAGMA table_info("' + self.table_name + '")')
self._columns = {}
for _, column, type_, notnull, hasdef, _ in self.cursor.fetchall():
column = re.sub(r'^\"|\"$', '', column)
match = re.match(r'(\w+)(\((.*?)\))?', type_)
if match:
typname = match.group(1).upper()
size = match.group(3) and int(match.group(3)) or None
else:
typname = type_.upper()
size = -1
self._columns[column] = {
'notnull': notnull,
'hasdef': hasdef,
'size': size,
'typname': typname,
}
# Fetch indexes defined for the table
try:
self.cursor.execute('PRAGMA index_list("' + self.table_name + '")')
except IndexError: # There is sometimes IndexError
self.cursor.execute('PRAGMA index_list("' + self.table_name + '")')
self._indexes = [l[1] for l in self.cursor.fetchall()]
# Keep track of which module created each field
self._field2module = {}
if self.object_name is not None:
self.cursor.execute('SELECT f.name, f.module '\
'FROM ir_model_field f '\
'JOIN ir_model m on (f.model=m.id) '\
'WHERE m.model = %s',
(self.object_name,)
)
for line in self.cursor.fetchall():
self._field2module[line[0]] = line[1]
def alter_size(self, column_name, column_type):
logging.getLogger('init').warning(
'Unable to alter size of column %s ' \
'of table %s not null!' % \
(column_name, self.table_name))
def alter_type(self, column_name, column_type):
logging.getLogger('init').warning(
'Unable to alter type of column %s ' \
'of table %s not null!' % \
(column_name, self.table_name))
def db_default(self, column_name, value):
logging.getLogger('init').warning(
'Unable to set default on column %s ' \
'of table %s not null!' % \
(column_name, self.table_name))
def add_raw_column(self, column_name, column_type, column_format,
default_fun=None, field_size=None, migrate=True):
if self.column_exist(column_name):
if not migrate:
return
base_type = column_type[0].upper()
if base_type != self._columns[column_name]['typname']:
if (self._columns[column_name]['typname'], base_type) in [
('VARCHAR', 'TEXT'),
('TEXT', 'VARCHAR'),
('DATE', 'TIMESTAMP'),
('INTEGER', 'FLOAT'),
]:
self.alter_type(column_name, base_type)
else:
logging.getLogger('init').warning(
'Unable to migrate column %s on table %s ' \
'from %s to %s.' % \
(column_name, self.table_name,
self._columns[column_name]['typname'], base_type))
if base_type == 'VARCHAR' \
and self._columns[column_name]['typname'] == 'VARCHAR':
# Migrate size
if field_size == None:
if self._columns[column_name]['size'] > 0:
self.alter_size(column_name, base_type)
pass
elif self._columns[column_name]['size'] == field_size:
pass
elif self._columns[column_name]['size'] > 0 and \
self._columns[column_name]['size'] < field_size:
self.alter_size(column_name, column_type[1])
else:
logging.getLogger('init').warning(
'Unable to migrate column %s on table %s ' \
'from varchar(%s) to varchar(%s).' % \
(column_name, self.table_name,
self._columns[column_name]['size'] > 0 and \
self._columns[column_name]['size'] or "",
field_size))
return
column_type = column_type[1]
default = ''
from trytond.model import fields
if isinstance(self._model._columns[column_name], (fields.Integer, fields.Float)):
default = ' DEFAULT 0'
self.cursor.execute(('ALTER TABLE "%s" ADD COLUMN "%s" %s' + default) %
(self.table_name, column_name, column_type))
if column_format:
# check if table is non-empty:
self.cursor.execute('SELECT 1 FROM "%s" limit 1' % self.table_name)
if self.cursor.fetchone():
# Populate column with default values:
default = None
if default_fun is not None:
default = default_fun(self.cursor, 0, {})
self.cursor.execute('UPDATE "' + self.table_name + '" '\
'SET "' + column_name + '" = %s',
(column_format(default),))
self._update_definitions()
def add_fk(self, column_name, reference, on_delete=None):
logging.getLogger('init').warning(
'Unable to add foreign key on table %s!' % \
(self.table_name,))
def index_action(self, column_name, action='add'):
if isinstance(column_name, basestring):
column_name = [column_name]
index_name = self.table_name + "_" + '_'.join(column_name) + "_index"
if action == 'add':
if index_name in self._indexes:
return
self.cursor.execute('CREATE INDEX "' + index_name + '" ' \
'ON "' + self.table_name + '" ( ' + \
','.join(['"' + x + '"' for x in column_name]) + \
')')
self._update_definitions()
elif action == 'remove':
if len(column_name) == 1:
if self._field2module.get(column_name[0]) != self.module_name:
return
if index_name in self._indexes:
self.cursor.execute('DROP INDEX "%s" ' % (index_name,))
self._update_definitions()
else:
raise Exception('Index action not supported!')
def not_null_action(self, column_name, action='add'):
if not self.column_exist(column_name):
return
if action == 'add':
logging.getLogger('init').warning(
'Unable to set not null on column %s ' \
'of table %s!' % \
(column_name, self.table_name))
elif action == 'remove':
logging.getLogger('init').warning(
'Unable to remove not null on column %s ' \
'of table %s!' % \
(column_name, self.table_name))
else:
raise Exception('Not null action not supported!')
def add_constraint(self, ident, constraint, exception=False):
logging.getLogger('init').warning(
'Unable to add constraint on table %s!' % \
(self.table_name,))
def drop_constraint(self, ident, exception=False):
logging.getLogger('init').warning(
'Unable to drop constraint on table %s!' % \
(self.table_name,))
def drop_column(self, column_name, exception=False):
logging.getLogger('init').warning(
'Unable to drop \'%s\' column on table %s!' % \
(column_name, self.table_name))
@staticmethod
def dropTable(cursor, model, table, cascade=False):
cursor.execute('DELETE from ir_model_data where '\
'model = \'%s\'' % model)
query = 'DROP TABLE "%s"' % table
if cascade:
query = query + ' CASCADE'
cursor.execute(query)
+1
View File
@@ -13,6 +13,7 @@ class ConfigManager(object):
'xmlport': 8069,
'webdav': False,
'webdavport': 8080,
'db_type': 'postgresql',
'db_host': False,
'db_port': False,
'db_name': False,