gooderp18绿色标准版
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

182 lines
7.0KB

  1. # -*- coding: utf-8 -*-
  2. # Part of Odoo. See LICENSE file for full copyright and licensing details.
  3. from psycopg2.extras import Json
  4. import logging
  5. from enum import IntEnum
  6. import odoo.modules
  7. _logger = logging.getLogger(__name__)
  8. def is_initialized(cr):
  9. """ Check if a database has been initialized for the ORM.
  10. The database can be initialized with the 'initialize' function below.
  11. """
  12. return odoo.tools.sql.table_exists(cr, 'ir_module_module')
  13. def initialize(cr):
  14. """ Initialize a database with for the ORM.
  15. This executes base/data/base_data.sql, creates the ir_module_categories
  16. (taken from each module descriptor file), and creates the ir_module_module
  17. and ir_model_data entries.
  18. """
  19. try:
  20. f = odoo.tools.misc.file_path('base/data/base_data.sql')
  21. except FileNotFoundError:
  22. m = "File not found: 'base.sql' (provided by module 'base')."
  23. _logger.critical(m)
  24. raise IOError(m)
  25. with odoo.tools.misc.file_open(f) as base_sql_file:
  26. cr.execute(base_sql_file.read()) # pylint: disable=sql-injection
  27. for i in odoo.modules.get_modules():
  28. mod_path = odoo.modules.get_module_path(i)
  29. if not mod_path:
  30. continue
  31. # This will raise an exception if no/unreadable descriptor file.
  32. info = odoo.modules.get_manifest(i)
  33. if not info:
  34. continue
  35. categories = info['category'].split('/')
  36. category_id = create_categories(cr, categories)
  37. if info['installable']:
  38. state = 'uninstalled'
  39. else:
  40. state = 'uninstallable'
  41. cr.execute('INSERT INTO ir_module_module \
  42. (author, website, name, shortdesc, description, \
  43. category_id, auto_install, state, web, license, application, icon, sequence, summary) \
  44. VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) RETURNING id', (
  45. info['author'],
  46. info['website'], i, Json({'en_US': info['name']}),
  47. Json({'en_US': info['description']}), category_id,
  48. info['auto_install'] is not False, state,
  49. info['web'],
  50. info['license'],
  51. info['application'], info['icon'],
  52. info['sequence'], Json({'en_US': info['summary']})))
  53. id = cr.fetchone()[0]
  54. cr.execute('INSERT INTO ir_model_data \
  55. (name,model,module, res_id, noupdate) VALUES (%s,%s,%s,%s,%s)', (
  56. 'module_'+i, 'ir.module.module', 'base', id, True))
  57. dependencies = info['depends']
  58. for d in dependencies:
  59. cr.execute(
  60. 'INSERT INTO ir_module_module_dependency (module_id, name, auto_install_required)'
  61. ' VALUES (%s, %s, %s)',
  62. (id, d, d in (info['auto_install'] or ()))
  63. )
  64. # Install recursively all auto-installing modules
  65. while True:
  66. # this selects all the auto_install modules whose auto_install_required
  67. # deps are marked as to install
  68. cr.execute("""
  69. SELECT m.name FROM ir_module_module m
  70. WHERE m.auto_install
  71. AND state not in ('to install', 'uninstallable')
  72. AND NOT EXISTS (
  73. SELECT 1 FROM ir_module_module_dependency d
  74. JOIN ir_module_module mdep ON (d.name = mdep.name)
  75. WHERE d.module_id = m.id
  76. AND d.auto_install_required
  77. AND mdep.state != 'to install'
  78. )""")
  79. to_auto_install = [x[0] for x in cr.fetchall()]
  80. # however if the module has non-required deps we need to install
  81. # those, so merge-in the modules which have a dependen*t* which is
  82. # *either* to_install or in to_auto_install and merge it in?
  83. cr.execute("""
  84. SELECT d.name FROM ir_module_module_dependency d
  85. JOIN ir_module_module m ON (d.module_id = m.id)
  86. JOIN ir_module_module mdep ON (d.name = mdep.name)
  87. WHERE (m.state = 'to install' OR m.name = any(%s))
  88. -- don't re-mark marked modules
  89. AND NOT (mdep.state = 'to install' OR mdep.name = any(%s))
  90. """, [to_auto_install, to_auto_install])
  91. to_auto_install.extend(x[0] for x in cr.fetchall())
  92. if not to_auto_install: break
  93. cr.execute("""UPDATE ir_module_module SET state='to install' WHERE name in %s""", (tuple(to_auto_install),))
  94. def create_categories(cr, categories):
  95. """ Create the ir_module_category entries for some categories.
  96. categories is a list of strings forming a single category with its
  97. parent categories, like ['Grand Parent', 'Parent', 'Child'].
  98. Return the database id of the (last) category.
  99. """
  100. p_id = None
  101. category = []
  102. while categories:
  103. category.append(categories[0])
  104. xml_id = 'module_category_' + ('_'.join(x.lower() for x in category)).replace('&', 'and').replace(' ', '_')
  105. # search via xml_id (because some categories are renamed)
  106. cr.execute("SELECT res_id FROM ir_model_data WHERE name=%s AND module=%s AND model=%s",
  107. (xml_id, "base", "ir.module.category"))
  108. c_id = cr.fetchone()
  109. if not c_id:
  110. cr.execute('INSERT INTO ir_module_category \
  111. (name, parent_id) \
  112. VALUES (%s, %s) RETURNING id', (Json({'en_US': categories[0]}), p_id))
  113. c_id = cr.fetchone()[0]
  114. cr.execute('INSERT INTO ir_model_data (module, name, res_id, model, noupdate) \
  115. VALUES (%s, %s, %s, %s, %s)', ('base', xml_id, c_id, 'ir.module.category', True))
  116. else:
  117. c_id = c_id[0]
  118. p_id = c_id
  119. categories = categories[1:]
  120. return p_id
  121. class FunctionStatus(IntEnum):
  122. MISSING = 0 # function is not present (falsy)
  123. PRESENT = 1 # function is present but not indexable (not immutable)
  124. INDEXABLE = 2 # function is present and indexable (immutable)
  125. def has_unaccent(cr):
  126. """ Test whether the database has function 'unaccent' and return its status.
  127. The unaccent is supposed to be provided by the PostgreSQL unaccent contrib
  128. module but any similar function will be picked by OpenERP.
  129. :rtype: FunctionStatus
  130. """
  131. cr.execute("""
  132. SELECT p.provolatile
  133. FROM pg_proc p
  134. LEFT JOIN pg_catalog.pg_namespace ns ON p.pronamespace = ns.oid
  135. WHERE p.proname = 'unaccent'
  136. AND p.pronargs = 1
  137. AND ns.nspname = 'public'
  138. """)
  139. result = cr.fetchone()
  140. if not result:
  141. return FunctionStatus.MISSING
  142. # The `provolatile` of unaccent allows to know whether the unaccent function
  143. # can be used to create index (it should be 'i' - means immutable), see
  144. # https://www.postgresql.org/docs/current/catalog-pg-proc.html.
  145. return FunctionStatus.INDEXABLE if result[0] == 'i' else FunctionStatus.PRESENT
  146. def has_trigram(cr):
  147. """ Test if the database has the a word_similarity function.
  148. The word_similarity is supposed to be provided by the PostgreSQL built-in
  149. pg_trgm module but any similar function will be picked by Odoo.
  150. """
  151. cr.execute("SELECT proname FROM pg_proc WHERE proname='word_similarity'")
  152. return len(cr.fetchall()) > 0
上海开阖软件有限公司 沪ICP备12045867号-1