Browse Source

Add sql_export_excel

12.0-mig-module_prototyper_last
Florian da Costa 5 years ago
parent
commit
8c305dd2fd
  1. 47
      sql_export_excel/README.rst
  2. 1
      sql_export_excel/__init__.py
  3. 20
      sql_export_excel/__openerp__.py
  4. 1
      sql_export_excel/models/__init__.py
  5. 108
      sql_export_excel/models/sql_export.py
  6. 4
      sql_export_excel/readme/CONFIGURE.rst
  7. 1
      sql_export_excel/readme/CONTRIBUTORS.rst
  8. 4
      sql_export_excel/readme/DESCRIPTION.rst
  9. 2
      sql_export_excel/tests/__init__.py
  10. 108
      sql_export_excel/tests/test_sql_query_excel.py
  11. 21
      sql_export_excel/views/sql_export_view.xml

47
sql_export_excel/README.rst

@ -0,0 +1,47 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:alt: License: AGPL-3
SQL Export Excel
================
Add the possibility to extract data from a sql query toward an excel file.
It is also possible to provide an template excel file for a query. In this case,
the data will be inserted in the specified sheet of the provided excel file. This
is usefull when doing a lot of calculation in excel and the data is coming from Odoo.
Known issues / Roadmap
======================
* It was designed to work with xlsx files only, xls format is not supported.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues
<https://github.com/OCA/server-tools/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smash it by providing detailed and welcomed feedback.
Credits
=======
Contributors
------------
* Florian da Costa <florian.dacosta@akretion.com>
Maintainer
----------
.. image:: http://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: http://odoo-community.org
This module is maintained by the OCA.
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
To contribute to this module, please visit http://odoo-community.org.

1
sql_export_excel/__init__.py

@ -0,0 +1 @@
from . import models

20
sql_export_excel/__openerp__.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Copyright 2019 Akretion
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{
'name': 'SQL Export Excel',
'version': '9.0.1.0.0',
'author': 'Akretion,Odoo Community Association (OCA)',
'website': 'http://github/oca/server-tools',
'license': 'AGPL-3',
'category': 'Generic Modules/Others',
'summary': 'Allow to export a sql query to an excel file.',
'depends': [
'sql_export',
],
'data': [
'views/sql_export_view.xml',
],
'installable': True,
}

1
sql_export_excel/models/__init__.py

@ -0,0 +1 @@
from . import sql_export

108
sql_export_excel/models/sql_export.py

@ -0,0 +1,108 @@
# -*- coding: utf-8 -*-
# Copyright 2019 Akretion
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from openerp import api, exceptions, fields, models, _
from cStringIO import StringIO
import logging
import base64
_logger = logging.getLogger(__name__)
try:
import openpyxl
except ImportError:
_logger.debug('Can not import openpyxl')
class SqlExport(models.Model):
_inherit = 'sql.export'
file_format = fields.Selection(
selection_add=[('excel', 'Excel')])
header = fields.Boolean(
default=True,
help="Indicate if the header should be exported to the file.")
attachment_id = fields.Many2one(
'ir.attachment', string='Excel Template',
help="If you configure an excel file (in xlsx format) here, the "
"result of the query will be injected in it.\nIt is usefull to "
"feed data in a excel file pre-configured with calculation")
sheet_position = fields.Integer(
default=1,
help="Indicate the sheet's position of the excel template where the "
"result of the sql query should be injected.")
row_position = fields.Integer(
default=1,
help="Indicate from which row the result of the query should be "
"injected.")
col_position = fields.Integer(
string="Column Position",
default=1,
help="Indicate from which column the result of the query should be "
"injected.")
@api.constrains('sheet_position')
def check_sheet_position(self):
for export in self:
if export.sheet_position < 1:
raise exceptions.ValidationError(
_("The sheet position can't be less than 1."))
@api.constrains('row_position')
def check_row_position(self):
for export in self:
if export.row_position < 1:
raise exceptions.ValidationError(
_("The row position can't be less than 1."))
@api.constrains('col_position')
def check_column_position(self):
for export in self:
if export.col_position < 1:
raise exceptions.ValidationError(
_("The column position can't be less than 1."))
@api.multi
def _get_file_extension(self):
self.ensure_one()
if self.file_format == 'excel':
return 'xlsx'
else:
return super(SqlExport, self)._get_file_extension()
@api.multi
def excel_get_datas_from_query(self, variable_dict):
self.ensure_one()
res = self._execute_sql_request(
params=variable_dict, mode='fetchall', header=self.header)
# Case we insert data in an existing excel file.
if self.attachment_id:
datas = self.attachment_id.datas
infile = StringIO()
infile.write(base64.b64decode(datas))
infile.seek(0)
wb = openpyxl.load_workbook(filename=infile)
sheets = wb.worksheets
try:
ws = sheets[self.sheet_position - 1]
except IndexError:
raise exceptions.ValidationError(
_("The Excel Template file contains less than %s sheets "
"Please, adjust the Sheet Position parameter."))
row_position = self.row_position or 1
col_position = self.col_position or 1
# Case of excel file creation
else:
wb = openpyxl.Workbook()
ws = wb.active
row_position = 1
col_position = 1
for index, row in enumerate(res, row_position):
for col, val in enumerate(row, col_position):
ws.cell(row=index, column=col).value = val
output = StringIO()
wb.save(output)
output.getvalue()
output_datas = base64.b64encode(output.getvalue())
output.close()
return output_datas

4
sql_export_excel/readme/CONFIGURE.rst

@ -0,0 +1,4 @@
If you want Odoo to update an existing excel file, you should create an attachment
with the excel file and configure this attachment on the query.
Then, you can configure the query to indicate if Odoo should export the header and where it should
insert the data. By default, it will insert it in the first sheet, at first row/column.

1
sql_export_excel/readme/CONTRIBUTORS.rst

@ -0,0 +1 @@
* Florian da Costa <florian.dacosta@akretion.com>

4
sql_export_excel/readme/DESCRIPTION.rst

@ -0,0 +1,4 @@
Add the possibility to extract data from a sql query toward an excel file.
It is also possible to provide an template excel file for a query. In this case,
the data will be inserted in the specified sheet of the provided excel file. This
is usefull when doing a lot of calculation in excel and the data is coming from Odoo.

2
sql_export_excel/tests/__init__.py

@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from . import test_sql_query_excel

108
sql_export_excel/tests/test_sql_query_excel.py

@ -0,0 +1,108 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2019 Akretion (<http://www.akretion.com>)
# @author: Florian da Costa
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp.tests.common import TransactionCase
import base64
from cStringIO import StringIO
import logging
_logger = logging.getLogger(__name__)
try:
import openpyxl
except ImportError:
_logger.debug('Can not import openpyxl')
class TestExportSqlQueryExcel(TransactionCase):
def setUp(self):
super(TestExportSqlQueryExcel, self).setUp()
self.wizard_obj = self.env['sql.file.wizard']
def get_workbook_from_query(self, wizard):
wizard.export_sql()
decoded_data = base64.b64decode(wizard.binary_file)
xlsx_file = StringIO(decoded_data)
return openpyxl.load_workbook(xlsx_file)
def test_excel_file_generation(self):
test_query = "SELECT 'testcol1' as firstcol, 2 as second_col"
query_vals = {
'name': 'Test Query Excel',
'query': test_query,
'file_format': 'excel'
}
query = self.env['sql.export'].create(query_vals)
query.button_validate_sql_expression()
wizard = self.wizard_obj.create({
'sql_export_id': query.id,
})
workbook = self.get_workbook_from_query(wizard)
ws = workbook.active
# Check values, header should be here by default
self.assertEqual(ws.cell(row=1, column=1).value, 'firstcol')
self.assertEqual(ws.cell(row=2, column=1).value, 'testcol1')
self.assertEqual(ws.cell(row=2, column=2).value, 2)
query.write({'header': False})
wb2 = self.get_workbook_from_query(wizard)
ws2 = wb2.active
# Check values, the header should not be present
self.assertEqual(ws2.cell(row=1, column=1).value, 'testcol1')
self.assertEqual(ws2.cell(row=1, column=2).value, 2)
def test_excel_file_insert(self):
# Create excel file with 2 sheets. Create a header in second sheet
# where data will be inserted
wb = openpyxl.Workbook()
ws = wb.active
ws.cell(row=1, column=1, value="My Test Value")
ws2 = wb.create_sheet("data")
ws2.cell(row=1, column=1, value='Partner Id')
ws2.cell(row=1, column=2, value='Partner Name')
output = StringIO()
wb.save(output)
data = output.getvalue()
# Create attachment with the created xlsx file which will be used as
# template in the sql query
attachmnent_vals = {
'name': 'template xlsx sql export Res Partner',
'datas': base64.b64encode(data),
}
attachment = self.env['ir.attachment'].create(attachmnent_vals)
# Create the query and configure it to insert the data in the second
# sheet of the xlsx template file and start inserting data at the
# second row, ignoring header (because the template excel file
# already contains a header)
test_query = "SELECT id, name FROM res_partner"
query_vals = {
'name': 'Test Query Excel',
'query': test_query,
'file_format': 'excel',
'attachment_id': attachment.id,
'sheet_position': 2,
'header': False,
'row_position': 2,
}
query = self.env['sql.export'].create(query_vals)
query.button_validate_sql_expression()
wizard = self.wizard_obj.create({
'sql_export_id': query.id,
})
# Check the generated excel file. The first sheet should still contain
# the same data and the second sheet should have kept the header and
# inserted data from the query
wb2 = self.get_workbook_from_query(wizard)
sheets = wb2.worksheets
ws1 = sheets[0]
# Check values, header should be here by default
self.assertEqual(ws1.cell(row=1, column=1).value, 'My Test Value')
ws2 = sheets[1]
self.assertEqual(ws2.cell(row=1, column=1).value, 'Partner Id')
self.assertTrue(ws2.cell(row=2, column=1).value)

21
sql_export_excel/views/sql_export_view.xml

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="sql_export_excel_view_form" model="ir.ui.view">
<field name="model">sql.export</field>
<field name="inherit_id" ref="sql_export.sql_export_view_form" />
<field name="arch" type="xml">
<field name="file_format" position="after">
<field name="header" attrs="{'invisible': [('file_format', '=', 'csv')]}"/>
<field name="attachment_id" attrs="{'invisible': [('file_format', '!=', 'excel')]}"/>
<field name="sheet_position" attrs="{'invisible': [('attachment_id', '=', False)]}"/>
<field name="row_position" attrs="{'invisible': [('attachment_id', '=', False)]}"/>
<field name="col_position" attrs="{'invisible': [('attachment_id', '=', False)]}"/>
</field>
</field>
</record>
</data>
</openerp>
Loading…
Cancel
Save