diff --git a/bin/addons/__init__.py b/bin/addons/__init__.py index 8d630b267b1..fcc60ca1809 100644 --- a/bin/addons/__init__.py +++ b/bin/addons/__init__.py @@ -275,12 +275,14 @@ def get_modules(): def create_graph(module_list, force=None): graph = Graph() - return upgrade_graph(graph, module_list, force) + upgrade_graph(graph, module_list, force) + return graph def upgrade_graph(graph, module_list, force=None): if force is None: force = [] packages = [] + len_graph = len(graph) for module in module_list: try: mod_path = get_module_path(module) @@ -326,8 +328,11 @@ def upgrade_graph(graph, module_list, force=None): for package in later: unmet_deps = filter(lambda p: p not in graph, dependencies[package]) logger.notifyChannel('init', netsvc.LOG_ERROR, 'module %s: Unmet dependencies: %s' % (package, ', '.join(unmet_deps))) - - return graph + + result = len(graph) - len_graph + if result != len(module_list): + logger.notifyChannel('init', netsvc.LOG_WARNING, 'Not all modules have loaded.') + return result def init_module_objects(cr, module_name, obj_list): @@ -682,14 +687,23 @@ def load_modules(db, force_demo=False, status=None, update_module=False): STATES_TO_LOAD += ['to install'] + loop_guardrail = 0 while True: + loop_guardrail += 1 + if loop_guardrail > 100: + raise ProgrammingError() cr.execute("SELECT name from ir_module_module WHERE state in (%s)" % ','.join(['%s']*len(STATES_TO_LOAD)), STATES_TO_LOAD) module_list = [name for (name,) in cr.fetchall() if name not in graph] if not module_list: break - - upgrade_graph(graph, module_list, force) + + new_modules_in_graph = upgrade_graph(graph, module_list, force) + if new_modules_in_graph == 0: + # nothing to load + break + + logger.notifyChannel('init', netsvc.LOG_DEBUG, 'Updating graph with %d more modules' % (len(module_list))) r = load_module_graph(cr, graph, status, report=report) has_updates = has_updates or r @@ -703,7 +717,7 @@ def load_modules(db, force_demo=False, status=None, update_module=False): obj = pool.get(model) if obj: obj._check_removed_columns(cr, log=True) - + if report.get_report(): logger.notifyChannel('init', netsvc.LOG_INFO, report) diff --git a/bin/addons/base/res/partner/partner.py b/bin/addons/base/res/partner/partner.py index f9e6d74c515..cfe1cb41710 100644 --- a/bin/addons/base/res/partner/partner.py +++ b/bin/addons/base/res/partner/partner.py @@ -32,12 +32,12 @@ class res_partner_function(osv.osv): _description = 'Function of the contact' _columns = { 'name': fields.char('Function Name', size=64, required=True), - 'code': fields.char('Code', size=8), - 'ref':fields.char('Notes', size=32, required=True), + 'code': fields.char('Code', size=8, required=True), + 'ref':fields.char('Notes', size=32,), } _order = 'name' _sql_constraints = [ - ('ref_uniq', 'unique (ref)', 'The Notes of the Partner Function must be unique !') + ('code_uniq', 'unique (code)', 'The Code of the Partner Function must be unique !') ] res_partner_function() diff --git a/bin/addons/base/res/partner/partner_data.xml b/bin/addons/base/res/partner/partner_data.xml index 1e66a8f2183..61286b755d3 100644 --- a/bin/addons/base/res/partner/partner_data.xml +++ b/bin/addons/base/res/partner/partner_data.xml @@ -10,13 +10,13 @@ Corp. Corp. - + partner Ltd ltd - + contact Madam @@ -32,18 +32,18 @@ Sir M. - + Director - CEO + CEO Chief Technical Officer - CTO + CTO Salesman - SAL + SAL diff --git a/bin/addons/base/rng/view.rng b/bin/addons/base/rng/view.rng index 6255c9f7650..40db5265c92 100644 --- a/bin/addons/base/rng/view.rng +++ b/bin/addons/base/rng/view.rng @@ -272,6 +272,7 @@ + diff --git a/bin/netsvc.py b/bin/netsvc.py index e4df96a2b0d..b9e4a589fab 100644 --- a/bin/netsvc.py +++ b/bin/netsvc.py @@ -93,7 +93,8 @@ def init_logger(): logger = logging.getLogger() # create a format for log messages and dates formatter = logging.Formatter('[%(asctime)s] %(levelname)s:%(name)s:%(message)s', '%a %b %d %Y %H:%M:%S') - + + logging_to_stdout = False if tools.config['syslog']: # SysLog Handler if os.name == 'nt': @@ -113,11 +114,13 @@ def init_logger(): os.makedirs(dirname) handler = logging.handlers.TimedRotatingFileHandler(logf,'D',1,30) except Exception, ex: - sys.stderr.write("ERROR: couldn't create the logfile directory\n") - handler = logging.StreamHandler(sys.stdout) + sys.stderr.write("ERROR: couldn't create the logfile directory. Logging to the standard output.\n") + handler = logging.StreamHandler(sys.stdout) + logging_to_stdout = True else: # Normal Handler on standard output handler = logging.StreamHandler(sys.stdout) + logging_to_stdout = True # tell the handler to use this format @@ -127,7 +130,7 @@ def init_logger(): logger.addHandler(handler) logger.setLevel(tools.config['log_level'] or '0') - if isinstance(handler, logging.StreamHandler) and os.name != 'nt': + if logging_to_stdout and os.name != 'nt': # change color of level names # uses of ANSI color codes # see http://pueblo.sourceforge.net/doc/manual/ansi_color_codes.html diff --git a/bin/osv/orm.py b/bin/osv/orm.py index 737fc40c194..ff48da700a0 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -40,6 +40,7 @@ # import time +import calendar import types import string import netsvc @@ -62,6 +63,12 @@ from tools.config import config regex_order = re.compile('^([a-zA-Z0-9_]+( desc)?( asc)?,?)+$', re.I) +def last_day_of_current_month(): + import datetime + import calendar + today = datetime.date.today() + last_day = str(calendar.monthrange(today.year, today.month)[1]) + return time.strftime('%Y-%m-' + last_day) def intersect(la, lb): return filter(lambda x: x in lb, la) diff --git a/bin/report/printscreen/ps_list.py b/bin/report/printscreen/ps_list.py index b59be30f1c8..d75d2ecad20 100644 --- a/bin/report/printscreen/ps_list.py +++ b/bin/report/printscreen/ps_list.py @@ -156,8 +156,15 @@ class report_printscreen_list(report_int): if fields[f]['type']=='many2one' and line[f]: line[f]= line[f][1] + if fields[f]['type']=='selection' and line[f]: + for key, value in fields[f]['selection']: + if key == line[f]: + line[f] = value + break + if fields[f]['type'] in ('one2many','many2many') and line[f]: line[f] = '( '+tools.ustr(len(line[f])) + ' )' + if fields[f]['type'] == 'float': precision=(('digits' in fields[f]) and fields[f]['digits'][1]) or 2 line[f]='%.2f'%(line[f]) diff --git a/bin/service/web_services.py b/bin/service/web_services.py index 44c853a6d4b..10a11a4819f 100644 --- a/bin/service/web_services.py +++ b/bin/service/web_services.py @@ -69,9 +69,9 @@ class db(netsvc.Service): self.actions[id] = {'clean': False} db = sql_db.db_connect('template1') - cr = db.cursor() + cr = db.serialized_cursor() try: - cr.autocommit(True) + cr.autocommit(True) # XXX inhibit the effect of a serialized cursor. is it what we want ? cr.execute('CREATE DATABASE "%s" ENCODING \'unicode\'' % db_name) finally: cr.close() @@ -149,8 +149,8 @@ class db(netsvc.Service): logger = netsvc.Logger() db = sql_db.db_connect('template1') - cr = db.cursor() - cr.autocommit(True) + cr = db.serialized_cursor() + cr.autocommit(True) # XXX inhibit the effect of a serialized cursor. is it what we want ? try: try: cr.execute('DROP DATABASE "%s"' % db_name) @@ -201,10 +201,10 @@ class db(netsvc.Service): raise Exception, "Database already exists" db = sql_db.db_connect('template1') - cr = db.cursor() - cr.autocommit(True) + cr = db.serialized_cursor() + cr.autocommit(True) # XXX inhibit the effect of a serialized cursor. is it what we want ? try: - cr.execute('CREATE DATABASE "%s" ENCODING \'unicode\'' % db_name) + cr.execute("""CREATE DATABASE "%s" ENCODING 'unicode' TEMPLATE "template0" """ % db_name) finally: cr.close() sql_db.close_db('template1') diff --git a/bin/sql_db.py b/bin/sql_db.py index b0212ac1ea4..ff9a6b0c2c6 100644 --- a/bin/sql_db.py +++ b/bin/sql_db.py @@ -21,7 +21,7 @@ ############################################################################## import netsvc -from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT, ISOLATION_LEVEL_SERIALIZABLE +from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT, ISOLATION_LEVEL_READ_COMMITTED, ISOLATION_LEVEL_SERIALIZABLE from psycopg2.pool import ThreadedConnectionPool from psycopg2.psycopg1 import cursor as psycopg1cursor @@ -76,8 +76,9 @@ class Cursor(object): return f(self, *args, **kwargs) return wrapper - def __init__(self, pool): + def __init__(self, pool, serialized=False): self._pool = pool + self._serialized = serialized self._cnx = pool.getconn() self._obj = self._cnx.cursor(cursor_factory=psycopg1cursor) self.autocommit(False) @@ -168,7 +169,8 @@ class Cursor(object): @check def autocommit(self, on): - self._cnx.set_isolation_level([ISOLATION_LEVEL_SERIALIZABLE, ISOLATION_LEVEL_AUTOCOMMIT][bool(on)]) + offlevel = [ISOLATION_LEVEL_READ_COMMITTED, ISOLATION_LEVEL_SERIALIZABLE][bool(self._serialized)] + self._cnx.set_isolation_level([offlevel, ISOLATION_LEVEL_AUTOCOMMIT][bool(on)]) @check def commit(self): @@ -190,6 +192,9 @@ class ConnectionPool(object): def cursor(self): return Cursor(self) + def serialized_cursor(self): + return Cursor(self, True) + def __getattr__(self, name): return getattr(self._pool, name) diff --git a/rpminstall_sh.txt b/rpminstall_sh.txt index 5694f250b5d..bb7bb3fc10c 100644 --- a/rpminstall_sh.txt +++ b/rpminstall_sh.txt @@ -7,6 +7,7 @@ # Need to overwrite the install-part of the RPM to append the # compression-suffix onto the filenames for the man-pages. # +python -c "import compileall, os; compileall.compile_dir(os.path.join(os.environ['PWD'], 'doc'), force=True)" python -O -c "import compileall, os; compileall.compile_dir(os.path.join(os.environ['PWD'], 'doc'), force=True)" python setup.py install --optimize 1 --root=$RPM_BUILD_ROOT --record=INSTALLED_FILES