[MERGE] latest trunk
[odoo/odoo.git] / openerp / modules / db.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
6 #    Copyright (C) 2010 OpenERP s.a. (<http://openerp.com>).
7 #
8 #    This program is free software: you can redistribute it and/or modify
9 #    it under the terms of the GNU Affero General Public License as
10 #    published by the Free Software Foundation, either version 3 of the
11 #    License, or (at your option) any later version.
12 #
13 #    This program is distributed in the hope that it will be useful,
14 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 #    GNU Affero General Public License for more details.
17 #
18 #    You should have received a copy of the GNU Affero General Public License
19 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 #
21 ##############################################################################
22
23 import openerp.modules
24 import logging
25
26 _logger = logging.getLogger(__name__)
27
28 def is_initialized(cr):
29     """ Check if a database has been initialized for the ORM.
30
31     The database can be initialized with the 'initialize' function below.
32
33     """
34     cr.execute("SELECT relname FROM pg_class WHERE relkind='r' AND relname='ir_module_module'")
35     return len(cr.fetchall()) > 0
36
37 def initialize(cr):
38     """ Initialize a database with for the ORM.
39
40     This executes base/base.sql, creates the ir_module_categories (taken
41     from each module descriptor file), and creates the ir_module_module
42     and ir_model_data entries.
43
44     """
45     f = openerp.modules.get_module_resource('base', 'base.sql')
46     if not f:
47         m = "File not found: 'base.sql' (provided by module 'base')."
48         _logger.critical(m)
49         raise IOError(m)
50     base_sql_file = openerp.tools.misc.file_open(f)
51     try:
52         cr.execute(base_sql_file.read())
53         cr.commit()
54     finally:
55         base_sql_file.close()
56
57     for i in openerp.modules.get_modules():
58         mod_path = openerp.modules.get_module_path(i)
59         if not mod_path:
60             continue
61
62         # This will raise an exception if no/unreadable descriptor file.
63         info = openerp.modules.load_information_from_description_file(i)
64
65         if not info:
66             continue
67         categories = info['category'].split('/')
68         category_id = create_categories(cr, categories)
69
70         if info['installable']:
71             state = 'uninstalled'
72         else:
73             state = 'uninstallable'
74
75         cr.execute('INSERT INTO ir_module_module \
76                 (author, website, name, shortdesc, description, \
77                     category_id, auto_install, state, certificate, web, license, application, icon, sequence, summary) \
78                 VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) RETURNING id', (
79             info['author'],
80             info['website'], i, info['name'],
81             info['description'], category_id,
82             info['auto_install'], state, info['certificate'],
83             info['web'],
84             info['license'],
85             info['application'], info['icon'],
86             info['sequence'], info['summary']))
87         id = cr.fetchone()[0]
88         cr.execute('INSERT INTO ir_model_data \
89             (name,model,module, res_id, noupdate) VALUES (%s,%s,%s,%s,%s)', (
90                 'module_'+i, 'ir.module.module', 'base', id, True))
91         dependencies = info['depends']
92         for d in dependencies:
93             cr.execute('INSERT INTO ir_module_module_dependency \
94                     (module_id,name) VALUES (%s, %s)', (id, d))
95
96     # Install recursively all auto-installing modules
97     while True:
98         cr.execute("""SELECT m.name FROM ir_module_module m WHERE m.auto_install AND state != 'to install'
99                       AND NOT EXISTS (
100                           SELECT 1 FROM ir_module_module_dependency d JOIN ir_module_module mdep ON (d.name = mdep.name)
101                                    WHERE d.module_id = m.id AND mdep.state != 'to install'
102                       )""")
103         to_auto_install = [x[0] for x in cr.fetchall()]
104         if not to_auto_install: break
105         cr.execute("""UPDATE ir_module_module SET state='to install' WHERE name in %s""", (tuple(to_auto_install),))
106
107     cr.commit()
108
109 def create_categories(cr, categories):
110     """ Create the ir_module_category entries for some categories.
111
112     categories is a list of strings forming a single category with its
113     parent categories, like ['Grand Parent', 'Parent', 'Child'].
114
115     Return the database id of the (last) category.
116
117     """
118     p_id = None
119     category = []
120     while categories:
121         category.append(categories[0])
122         if p_id is not None:
123             cr.execute('SELECT id \
124                        FROM ir_module_category \
125                        WHERE name=%s AND parent_id=%s', (categories[0], p_id))
126         else:
127             cr.execute('SELECT id \
128                        FROM ir_module_category \
129                        WHERE name=%s AND parent_id IS NULL', (categories[0],))
130         c_id = cr.fetchone()
131         if not c_id:
132             cr.execute('INSERT INTO ir_module_category \
133                     (name, parent_id) \
134                     VALUES (%s, %s) RETURNING id', (categories[0], p_id))
135             c_id = cr.fetchone()[0]
136             xml_id = 'module_category_' + ('_'.join(map(lambda x: x.lower(), category))).replace('&', 'and').replace(' ', '_')
137             cr.execute('INSERT INTO ir_model_data (module, name, res_id, model) \
138                        VALUES (%s, %s, %s, %s)', ('base', xml_id, c_id, 'ir.module.category'))
139         else:
140             c_id = c_id[0]
141         p_id = c_id
142         categories = categories[1:]
143     return p_id
144
145 def has_unaccent(cr):
146     """ Test if the database has an unaccent function.
147
148     The unaccent is supposed to be provided by the PostgreSQL unaccent contrib
149     module but any similar function will be picked by OpenERP.
150
151     """
152     cr.execute("SELECT proname FROM pg_proc WHERE proname='unaccent'")
153     return len(cr.fetchall()) > 0
154
155 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: