Browse Source

Fix several issues

Fix error '... is not a table or foreign table'
Fix view (colors + oe_highlight)
Fix tests
Fix README + manifest
Fix back to draft
Fix cron call + default values
Use Postgres version 9.6 for travis builds
pull/264/head
Patrick Tombez 7 years ago
committed by Adrià Gil Sorribes
parent
commit
79a5457614
  1. 2
      bi_sql_editor/README.rst
  2. 5
      bi_sql_editor/__manifest__.py
  3. 54
      bi_sql_editor/models/bi_sql_view.py
  4. 64
      bi_sql_editor/tests/test_bi_sql_view.py
  5. 6
      bi_sql_editor/views/view_bi_sql_view.xml

2
bi_sql_editor/README.rst

@ -1,4 +1,4 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3 :alt: License: AGPL-3

5
bi_sql_editor/__manifest__.py

@ -5,13 +5,14 @@
{ {
'name': 'BI SQL Editor', 'name': 'BI SQL Editor',
'summary': "BI Views builder, based on Materialized or Normal SQL Views",
'summary': 'BI Views builder, based on Materialized or Normal SQL Views',
'version': '10.0.1.0.0', 'version': '10.0.1.0.0',
'license': 'AGPL-3', 'license': 'AGPL-3',
'category': 'Reporting', 'category': 'Reporting',
'author': 'GRAP,Odoo Community Association (OCA)', 'author': 'GRAP,Odoo Community Association (OCA)',
'website': 'https://www.odoo-community.org',
'website': 'https://github.com/OCA/reporting-engine',
'depends': [ 'depends': [
'base',
'sql_request_abstract', 'sql_request_abstract',
], ],
'data': [ 'data': [

54
bi_sql_editor/models/bi_sql_view.py

@ -13,6 +13,22 @@ from odoo.exceptions import UserError
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
class BaseModel(models.AbstractModel):
_inherit = 'base'
@api.model_cr_context
def _auto_init(self):
if self._name.startswith(BiSQLView._model_prefix):
self._auto = False
return super(BaseModel, self)._auto_init()
@api.model_cr_context
def _auto_end(self):
if self._name.startswith(BiSQLView._model_prefix):
self._foreign_keys = set()
return super(BaseModel, self)._auto_end()
class BiSQLView(models.Model): class BiSQLView(models.Model):
_name = 'bi.sql.view' _name = 'bi.sql.view'
_inherit = ['sql.request.mixin'] _inherit = ['sql.request.mixin']
@ -161,10 +177,7 @@ class BiSQLView(models.Model):
# Overload Section # Overload Section
@api.multi @api.multi
def unlink(self): def unlink(self):
non_draft_views = self.search([
('id', 'in', self.ids),
('state', 'not in', ('draft', 'sql_valid'))])
if non_draft_views:
if any(view.state not in ('draft', 'sql_valid') for view in self):
raise UserError(_("You can only unlink draft views")) raise UserError(_("You can only unlink draft views"))
return super(BiSQLView, self).unlink() return super(BiSQLView, self).unlink()
@ -201,6 +214,15 @@ class BiSQLView(models.Model):
@api.multi @api.multi
def button_set_draft(self): def button_set_draft(self):
for sql_view in self: for sql_view in self:
sql_view.menu_id.unlink()
sql_view.action_id.unlink()
sql_view.tree_view_id.unlink()
sql_view.graph_view_id.unlink()
sql_view.pivot_view_id.unlink()
sql_view.search_view_id.unlink()
if sql_view.cron_id:
sql_view.cron_id.unlink()
if sql_view.state in ('model_valid', 'ui_valid'): if sql_view.state in ('model_valid', 'ui_valid'):
# Drop SQL View (and indexes by cascade) # Drop SQL View (and indexes by cascade)
if sql_view.is_materialized: if sql_view.is_materialized:
@ -209,14 +231,6 @@ class BiSQLView(models.Model):
# Drop ORM # Drop ORM
sql_view._drop_model_and_fields() sql_view._drop_model_and_fields()
sql_view.tree_view_id.unlink()
sql_view.graph_view_id.unlink()
sql_view.pivot_view_id.unlink()
sql_view.search_view_id.unlink()
sql_view.action_id.unlink()
sql_view.menu_id.unlink()
if sql_view.cron_id:
sql_view.cron_id.unlink()
sql_view.write({'state': 'draft', 'has_group_changed': False}) sql_view.write({'state': 'draft', 'has_group_changed': False})
@api.multi @api.multi
@ -293,7 +307,8 @@ class BiSQLView(models.Model):
'name': _('Refresh Materialized View %s') % (self.view_name), 'name': _('Refresh Materialized View %s') % (self.view_name),
'user_id': SUPERUSER_ID, 'user_id': SUPERUSER_ID,
'model': 'bi.sql.view', 'model': 'bi.sql.view',
'function': 'button_refresh_materialized_view',
'function': '_refresh_materialized_view_cron',
'numbercall': -1,
'args': repr(([self.id],)) 'args': repr(([self.id],))
} }
@ -447,7 +462,7 @@ class BiSQLView(models.Model):
self._prepare_rule()).id self._prepare_rule()).id
# Drop table, created by the ORM # Drop table, created by the ORM
req = "DROP TABLE %s" % (sql_view.view_name) req = "DROP TABLE %s" % (sql_view.view_name)
self.env.cr.execute(req)
self._log_execute(req)
@api.multi @api.multi
def _create_model_access(self): def _create_model_access(self):
@ -467,7 +482,7 @@ class BiSQLView(models.Model):
if sql_view.rule_id: if sql_view.rule_id:
sql_view.rule_id.unlink() sql_view.rule_id.unlink()
if sql_view.model_id: if sql_view.model_id:
sql_view.model_id.unlink()
sql_view.model_id.with_context(_force_unlink=True).unlink()
@api.multi @api.multi
def _hook_executed_request(self): def _hook_executed_request(self):
@ -481,7 +496,7 @@ class BiSQLView(models.Model):
AND NOT attisdropped AND NOT attisdropped
AND attnum > 0 AND attnum > 0
ORDER BY attnum;""" % (self.view_name) ORDER BY attnum;""" % (self.view_name)
self.env.cr.execute(req)
self._log_execute(req)
return self.env.cr.fetchall() return self.env.cr.fetchall()
@api.multi @api.multi
@ -548,6 +563,11 @@ class BiSQLView(models.Model):
return columns return columns
@api.model
def _refresh_materialized_view_cron(self, view_ids):
sql_views = self.browse(view_ids)
return sql_views._refresh_materialized_view()
@api.multi @api.multi
def _refresh_materialized_view(self): def _refresh_materialized_view(self):
for sql_view in self: for sql_view in self:
@ -568,7 +588,7 @@ class BiSQLView(models.Model):
for sql_view in self: for sql_view in self:
req = "SELECT pg_size_pretty(pg_total_relation_size('%s'));" % ( req = "SELECT pg_size_pretty(pg_total_relation_size('%s'));" % (
sql_view.view_name) sql_view.view_name)
self.env.cr.execute(req)
self._log_execute(req)
sql_view.size = self.env.cr.fetchone()[0] sql_view.size = self.env.cr.fetchone()[0]
@api.multi @api.multi

64
bi_sql_editor/tests/test_bi_sql_view.py

@ -2,56 +2,68 @@
# Copyright 2017 Onestein (<http://www.onestein.eu>) # Copyright 2017 Onestein (<http://www.onestein.eu>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo.tests.common import TransactionCase, at_install, post_install
from odoo.exceptions import AccessError
from odoo.tests.common import SingleTransactionCase, at_install, post_install
from odoo.exceptions import AccessError, UserError
class TestBiSqlViewEditor(TransactionCase):
@at_install(False)
@post_install(True)
class TestBiSqlViewEditor(SingleTransactionCase):
@classmethod
def setUpClass(cls):
super(TestBiSqlViewEditor, cls).setUpClass()
def setUp(self):
super(TestBiSqlViewEditor, self).setUp()
self.res_partner = self.env['res.partner']
self.res_users = self.env['res.users']
self.bi_sql_view = self.env['bi.sql.view']
self.group_bi_user = self.env.ref(
cls.res_partner = cls.env['res.partner']
cls.res_users = cls.env['res.users']
cls.bi_sql_view = cls.env['bi.sql.view']
cls.group_bi_user = cls.env.ref(
'sql_request_abstract.group_sql_request_manager') 'sql_request_abstract.group_sql_request_manager')
self.group_user = self.env.ref(
cls.group_user = cls.env.ref(
'base.group_user') 'base.group_user')
self.view = self.bi_sql_view.create({
cls.view = cls.bi_sql_view.create({
'name': 'Partners View 2', 'name': 'Partners View 2',
'is_materialized': False,
'is_materialized': True,
'technical_name': 'partners_view_2', 'technical_name': 'partners_view_2',
'query': "SELECT name as x_name, street as x_street," 'query': "SELECT name as x_name, street as x_street,"
"company_id as x_company_id FROM res_partner " "company_id as x_company_id FROM res_partner "
"ORDER BY name" "ORDER BY name"
}) })
self.company = self.env.ref('base.main_company')
cls.company = cls.env.ref('base.main_company')
# Create bi user # Create bi user
self.bi_user = self._create_user('bi_user', [self.group_bi_user],
self.company)
self.no_bi_user = self._create_user('no_bi_user', [self.group_user],
self.company)
cls.bi_user = cls._create_user('bi_user', cls.group_bi_user,
cls.company)
cls.no_bi_user = cls._create_user('no_bi_user', cls.group_user,
cls.company)
def _create_user(self, login, groups, company):
@classmethod
def _create_user(cls, login, groups, company):
"""Create a user.""" """Create a user."""
group_ids = [group.id for group in groups]
user = self.res_users.create({
user = cls.res_users.create({
'name': 'Test BI User', 'name': 'Test BI User',
'login': login, 'login': login,
'password': 'demo', 'password': 'demo',
'email': 'example@yourcompany.com', 'email': 'example@yourcompany.com',
'notify_email': 'none',
'company_id': company.id, 'company_id': company.id,
'groups_id': [(6, 0, group_ids)]
'groups_id': [(6, 0, groups.ids)]
}) })
return user return user
@at_install(False)
@post_install(True)
def test_process_view(self): def test_process_view(self):
view = self.view view = self.view
self.assertEqual(view.state, 'draft', 'state not draft') self.assertEqual(view.state, 'draft', 'state not draft')
view.button_validate_sql_expression() view.button_validate_sql_expression()
self.assertEqual(view.state, 'sql_valid', 'state not sql_valid') self.assertEqual(view.state, 'sql_valid', 'state not sql_valid')
view.button_create_sql_view_and_model()
self.assertEqual(view.state, 'model_valid', 'state not model_valid')
view.button_create_ui()
self.assertEqual(view.state, 'ui_valid', 'state not ui_valid')
view.button_update_model_access()
self.assertEqual(view.has_group_changed, False,
'has_group_changed not False')
cron_res = view.cron_id.method_direct_trigger()
self.assertEqual(cron_res, True, 'something went wrong with the cron')
def test_copy(self): def test_copy(self):
copy_view = self.view.copy() copy_view = self.view.copy()
@ -68,8 +80,10 @@ class TestBiSqlViewEditor(TransactionCase):
'bi %s' % self.view.name) 'bi %s' % self.view.name)
def test_unlink(self): def test_unlink(self):
self.assertEqual(self.view.state, 'draft', 'state not draft')
self.view.button_validate_sql_expression()
self.assertEqual(self.view.state, 'ui_valid', 'state not ui_valid')
with self.assertRaises(UserError):
self.view.unlink()
self.view.button_set_draft()
self.view.unlink() self.view.unlink()
res = self.bi_sql_view.search([('name', '=', 'Partners View 2')]) res = self.bi_sql_view.search([('name', '=', 'Partners View 2')])
self.assertEqual(len(res), 0, 'View not deleted') self.assertEqual(len(res), 0, 'View not deleted')

6
bi_sql_editor/views/view_bi_sql_view.xml

@ -10,7 +10,7 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
<record id="view_bi_sql_view_tree" model="ir.ui.view"> <record id="view_bi_sql_view_tree" model="ir.ui.view">
<field name="model">bi.sql.view</field> <field name="model">bi.sql.view</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree colors="blue:state=='draft'; brown:state in ('sql_valid', 'model_valid')">
<tree decoration-info="state=='draft'" decoration-warning="state in ('sql_valid', 'model_valid')">
<field name="name"/> <field name="name"/>
<field name="technical_name"/> <field name="technical_name"/>
<field name="size"/> <field name="size"/>
@ -29,7 +29,7 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
<button name="button_set_draft" type="object" states="sql_valid,model_valid,ui_valid" <button name="button_set_draft" type="object" states="sql_valid,model_valid,ui_valid"
string="Set to Draft" groups="sql_request_abstract.group_sql_request_manager" string="Set to Draft" groups="sql_request_abstract.group_sql_request_manager"
confirm="Are you sure you want to set to draft this SQL View. It will delete the materialized view, and all the previous mapping realized with the columns"/> confirm="Are you sure you want to set to draft this SQL View. It will delete the materialized view, and all the previous mapping realized with the columns"/>
<button name="button_preview_sql_expression" type="object" states="draft" string="Preview SQL Expression" class="oe_highlight"/>
<button name="button_preview_sql_expression" type="object" states="draft" string="Preview SQL Expression" />
<button name="button_create_sql_view_and_model" type="object" states="sql_valid" <button name="button_create_sql_view_and_model" type="object" states="sql_valid"
string="Create SQL View, Indexes and Models" class="oe_highlight" string="Create SQL View, Indexes and Models" class="oe_highlight"
help="This will try to create an SQL View, based on the SQL request and the according Transient Model and fields, based on settings"/> help="This will try to create an SQL View, based on the SQL request and the according Transient Model and fields, based on settings"/>
@ -70,7 +70,7 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
</page> </page>
<page string="SQL Fields" attrs="{'invisible': [('state', '=', 'draft')]}"> <page string="SQL Fields" attrs="{'invisible': [('state', '=', 'draft')]}">
<field name="bi_sql_view_field_ids" nolabel="1" colspan="4" attrs="{'readonly': [('state', '!=', 'sql_valid')]}"> <field name="bi_sql_view_field_ids" nolabel="1" colspan="4" attrs="{'readonly': [('state', '!=', 'sql_valid')]}">
<tree editable="bottom" colors="blue:field_description==False">
<tree editable="bottom" decoration-info="field_description==False">
<field name="sequence"/> <field name="sequence"/>
<field name="name"/> <field name="name"/>
<field name="sql_type"/> <field name="sql_type"/>

Loading…
Cancel
Save