Browse Source

Merge pull request #2 from luc-demeyer/6.1

PEP8 80 char line length
6.1
Yannick Vaucher 10 years ago
parent
commit
8f999af144
  1. 6
      report_xls/__init__.py
  2. 65
      report_xls/__openerp__.py
  3. 130
      report_xls/report_xls.py
  4. 18
      report_xls/utils.py

6
report_xls/__init__.py

@ -3,7 +3,7 @@
# #
# OpenERP, Open Source Management Solution # OpenERP, Open Source Management Solution
# #
# Copyright (c) 2013 Noviat nv/sa (www.noviat.com). All rights reserved.
# Copyright (c) 2013-2014 Noviat nv/sa (www.noviat.com). All rights reserved.
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as # it under the terms of the GNU Affero General Public License as
@ -12,11 +12,11 @@
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details. # GNU Affero General Public License for more details.
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################

65
report_xls/__openerp__.py

@ -3,7 +3,7 @@
# #
# OpenERP, Open Source Management Solution # OpenERP, Open Source Management Solution
# #
# Copyright (c) 2013 Noviat nv/sa (www.noviat.com). All rights reserved.
# Copyright (c) 2013-2014 Noviat nv/sa (www.noviat.com). All rights reserved.
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as # it under the terms of the GNU Affero General Public License as
@ -12,31 +12,70 @@
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details. # GNU Affero General Public License for more details.
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
{ {
'name': 'XLS report engine',
'version': '0.3',
'name': 'Excel report engine',
'version': '0.6',
'license': 'AGPL-3', 'license': 'AGPL-3',
'author': 'Noviat', 'author': 'Noviat',
'website': 'http://www.noviat.com', 'website': 'http://www.noviat.com',
'category': 'Reporting', 'category': 'Reporting',
'description': """
'description': """
Excel report engine
===================
This module adds Excel export capabilities to the standard OpenERP reporting
engine.
Report development
''''''''''''''''''
In order to create an Excel report you can\n
- define a report of type 'xls'
- pass ``{'xls_export': 1}`` via the context to the report create method
The ``report_xls`` class contains a number of attributes and methods to
facilitate the creation XLS reports in OpenERP.
* cell types
Supported cell types : text, number, boolean, date.
* cell styles
The predefined cell style definitions result in a consistent
look and feel of the OpenERP Excel reports.
* cell formulas
Cell formulas can be easily added with the help of the ``rowcol_to_cell()``
function which you can import from the ``utils.py`` module.
* Excel templates
It is possible to define Excel templates which can be adapted
by 'inherited' modules.
Download the ``account_move_line_report_xls`` module
from http://apps.openerp.com as example.
* XLS with multiple sheets
Download the ``account_journal_report_xls`` module
from http://apps.openerp.com as example.
Development assistance
''''''''''''''''''''''
Contact info@noviat.com for help with the development of
Excel reports in OpenERP.
This module adds XLS export capabilities to the standard OpenERP reporting engine.
In order to generate an XLS export you can define a report of type 'xls' or alternatively pass {'xls_export' : 1) via the context to create method of the report.
""", """,
'depends': ['base'], 'depends': ['base'],
'demo_xml': [],
'init_xml': [],
'update_xml' : [],
'external_dependencies': {'python': ['xlwt']},
'active': False, 'active': False,
'installable': True, 'installable': True,
} }

130
report_xls/report_xls.py

@ -2,8 +2,8 @@
############################################################################## ##############################################################################
# #
# OpenERP, Open Source Management Solution # OpenERP, Open Source Management Solution
#
#Copyright (c) 2013 Noviat nv/sa (www.noviat.com). All rights reserved.
#
# Copyright (c) 2013-2014 Noviat nv/sa (www.noviat.com). All rights reserved.
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as # it under the terms of the GNU Affero General Public License as
@ -12,33 +12,36 @@
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details. # GNU Affero General Public License for more details.
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
import xlwt import xlwt
from xlwt.Style import default_style from xlwt.Style import default_style
import cStringIO import cStringIO
import datetime, time
from datetime import datetime
from openerp.osv.fields import datetime as datetime_field
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
import inspect import inspect
from types import CodeType from types import CodeType
from openerp.report.report_sxw import *
from openerp.report.report_sxw import report_sxw
from openerp import pooler from openerp import pooler
from openerp.tools.translate import translate, _
import logging import logging
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
class AttrDict(dict): class AttrDict(dict):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(AttrDict, self).__init__(*args, **kwargs) super(AttrDict, self).__init__(*args, **kwargs)
self.__dict__ = self self.__dict__ = self
class report_xls(report_sxw): class report_xls(report_sxw):
xls_types = { xls_types = {
'bool': xlwt.Row.set_cell_boolean, 'bool': xlwt.Row.set_cell_boolean,
'date': xlwt.Row.set_cell_date, 'date': xlwt.Row.set_cell_date,
@ -53,36 +56,32 @@ class report_xls(report_sxw):
} }
# TO DO: move parameters infra to configurable data # TO DO: move parameters infra to configurable data
# header/footer # header/footer
DT_FORMAT = '%Y-%m-%d %H:%M:%S'
hf_params = { hf_params = {
'font_size': 8,
'font_style': 'I', # B: Bold, I: Italic, U: Underline
}
xls_headers = {
'standard': ''
'font_size': 8,
'font_style': 'I', # B: Bold, I: Italic, U: Underline
} }
xls_footers = {
'standard': ('&L&%(font_size)s&%(font_style)s' + datetime.now().strftime(DT_FORMAT) +
'&R&%(font_size)s&%(font_style)s&P / &N') %hf_params
}
# styles # styles
_pfc = '26' # default pattern fore_color
_bc = '22' # borders color
_pfc = '26' # default pattern fore_color
_bc = '22' # borders color
decimal_format = '#,##0.00' decimal_format = '#,##0.00'
date_format = 'YYYY-MM-DD'
date_format = 'YYYY-MM-DD'
xls_styles = { xls_styles = {
'xls_title': 'font: bold true, height 240;', 'xls_title': 'font: bold true, height 240;',
'bold': 'font: bold true;', 'bold': 'font: bold true;',
'underline': 'font: underline true;', 'underline': 'font: underline true;',
'italic': 'font: italic true;', 'italic': 'font: italic true;',
'fill': 'pattern: pattern solid, fore_color %s;' %_pfc,
'fill_blue' : 'pattern: pattern solid, fore_color 27;',
'fill_grey' : 'pattern: pattern solid, fore_color 22;',
'borders_all': 'borders: left thin, right thin, top thin, bottom thin, ' \
'left_colour %s, right_colour %s, top_colour %s, bottom_colour %s;' %(_bc,_bc,_bc,_bc),
'fill': 'pattern: pattern solid, fore_color %s;' % _pfc,
'fill_blue': 'pattern: pattern solid, fore_color 27;',
'fill_grey': 'pattern: pattern solid, fore_color 22;',
'borders_all':
'borders: '
'left thin, right thin, top thin, bottom thin, '
'left_colour %s, right_colour %s, '
'top_colour %s, bottom_colour %s;'
% (_bc, _bc, _bc, _bc),
'left': 'align: horz left;', 'left': 'align: horz left;',
'center': 'align: horz center;', 'center': 'align: horz center;',
'right': 'align: horz right;', 'right': 'align: horz right;',
@ -91,27 +90,32 @@ class report_xls(report_sxw):
'bottom': 'align: vert bottom;', 'bottom': 'align: vert bottom;',
} }
# TO DO: move parameters supra to configurable data # TO DO: move parameters supra to configurable data
def create(self, cr, uid, ids, data, context=None):
def create(self, cr, uid, ids, data, context=None):
self.pool = pooler.get_pool(cr.dbname) self.pool = pooler.get_pool(cr.dbname)
self.cr = cr self.cr = cr
self.uid = uid self.uid = uid
report_obj = self.pool.get('ir.actions.report.xml') report_obj = self.pool.get('ir.actions.report.xml')
report_ids = report_obj.search(cr, uid,
[('report_name', '=', self.name[7:])], context=context)
report_ids = report_obj.search(
cr, uid, [('report_name', '=', self.name[7:])], context=context)
if report_ids: if report_ids:
report_xml = report_obj.browse(cr, uid, report_ids[0], context=context)
report_xml = report_obj.browse(
cr, uid, report_ids[0], context=context)
self.title = report_xml.name self.title = report_xml.name
if report_xml.report_type == 'xls': if report_xml.report_type == 'xls':
return self.create_source_xls(cr, uid, ids, data, context) return self.create_source_xls(cr, uid, ids, data, context)
elif context.get('xls_export'): elif context.get('xls_export'):
# use model from 'data' when no ir.actions.report.xml entry
self.table = data.get('model') or self.table
return self.create_source_xls(cr, uid, ids, data, context) return self.create_source_xls(cr, uid, ids, data, context)
return super(report_xls, self).create(cr, uid, ids, data, context) return super(report_xls, self).create(cr, uid, ids, data, context)
def create_source_xls(self, cr, uid, ids, data, context=None): def create_source_xls(self, cr, uid, ids, data, context=None):
if not context: context = {}
if not context:
context = {}
parser_instance = self.parser(cr, uid, self.name2, context) parser_instance = self.parser(cr, uid, self.name2, context)
self.parser_instance = parser_instance self.parser_instance = parser_instance
self.context = context
objs = self.getObjects(cr, uid, ids, context) objs = self.getObjects(cr, uid, ids, context)
parser_instance.set_context(objs, data, ids, 'xls') parser_instance.set_context(objs, data, ids, 'xls')
objs = parser_instance.localcontext['objects'] objs = parser_instance.localcontext['objects']
@ -119,33 +123,45 @@ class report_xls(report_sxw):
wb = xlwt.Workbook(encoding='utf-8') wb = xlwt.Workbook(encoding='utf-8')
_p = AttrDict(parser_instance.localcontext) _p = AttrDict(parser_instance.localcontext)
_xs = self.xls_styles _xs = self.xls_styles
self.xls_headers = {
'standard': '',
}
report_date = datetime_field.context_timestamp(
cr, uid, datetime.now(), context)
report_date = report_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
self.xls_footers = {
'standard': (
'&L&%(font_size)s&%(font_style)s' + report_date +
'&R&%(font_size)s&%(font_style)s&P / &N'
) % self.hf_params,
}
self.generate_xls_report(_p, _xs, data, objs, wb) self.generate_xls_report(_p, _xs, data, objs, wb)
wb.save(n) wb.save(n)
n.seek(0) n.seek(0)
return (n.read(), 'xls')
return (n.read(), 'xls')
def render(self, wanted, col_specs, rowtype, render_space='empty'): def render(self, wanted, col_specs, rowtype, render_space='empty'):
""" """
returns 'mako'-rendered col_specs
returns 'evaluated' col_specs
Input: Input:
- wanted: element from the wanted_list - wanted: element from the wanted_list
- col_specs : cf. specs[1:] documented in xls_row_template method - col_specs : cf. specs[1:] documented in xls_row_template method
- rowtype : 'header' or 'data' - rowtype : 'header' or 'data'
- render_space : type dict, (caller_space + localcontext) if not specified
- render_space : type dict, (caller_space + localcontext)
if not specified
""" """
if render_space == 'empty': if render_space == 'empty':
render_space = {} render_space = {}
caller_space = inspect.currentframe().f_back.f_back.f_locals caller_space = inspect.currentframe().f_back.f_back.f_locals
localcontext = self.parser_instance.localcontext localcontext = self.parser_instance.localcontext
render_space.update(caller_space) render_space.update(caller_space)
render_space.update(localcontext)
render_space.update(localcontext)
row = col_specs[wanted][rowtype][:] row = col_specs[wanted][rowtype][:]
for i in range(len(row)): for i in range(len(row)):
if isinstance(row[i], CodeType): if isinstance(row[i], CodeType):
row[i] = eval(row[i], render_space) row[i] = eval(row[i], render_space)
row.insert(0, wanted) row.insert(0, wanted)
#_logger.warn('row O = %s', row)
return row return row
def generate_xls_report(self, parser, xls_styles, data, objects, wb): def generate_xls_report(self, parser, xls_styles, data, objects, wb):
@ -155,13 +171,15 @@ class report_xls(report_sxw):
def xls_row_template(self, specs, wanted_list): def xls_row_template(self, specs, wanted_list):
""" """
Returns a row template. Returns a row template.
Input : Input :
- 'wanted_list': list of Columns that will be returned in the row_template
- 'wanted_list': list of Columns that will be returned in the
row_template
- 'specs': list with Column Characteristics - 'specs': list with Column Characteristics
0: Column Name (from wanted_list) 0: Column Name (from wanted_list)
1: Column Colspan 1: Column Colspan
2: Column Size (unit = the width of the character 0 as it appears in the sheets default font)
2: Column Size (unit = the width of the character 0
as it appears in the sheets default font)
3: Column Type 3: Column Type
4: Column Data 4: Column Data
5: Column Formula (or 'None' for Data) 5: Column Formula (or 'None' for Data)
@ -180,7 +198,8 @@ class report_xls(report_sxw):
if s_len > 5 and s[5] is not None: if s_len > 5 and s[5] is not None:
c.append({'formula': s[5]}) c.append({'formula': s[5]})
else: else:
c.append({'write_cell_func': report_xls.xls_types[c[3]]})
c.append({
'write_cell_func': report_xls.xls_types[c[3]]})
# Set custom cell style # Set custom cell style
if s_len > 6 and s[6] is not None: if s_len > 6 and s[6] is not None:
c.append(s[6]) c.append(s[6])
@ -190,28 +209,33 @@ class report_xls(report_sxw):
if s_len > 7 and s[7] is not None: if s_len > 7 and s[7] is not None:
c.append(s[7]) c.append(s[7])
else: else:
c.append(None)
c.append(None)
r.append((col, c[1], c)) r.append((col, c[1], c))
col += c[1] col += c[1]
break break
if not found: if not found:
_logger.warn("report_xls.xls_row_template, column '%s' not found in specs", w)
_logger.warn("report_xls.xls_row_template, "
"column '%s' not found in specs", w)
return r return r
def xls_write_row(self, ws, row_pos, row_data, row_style=default_style, set_column_size=False):
def xls_write_row(self, ws, row_pos, row_data,
row_style=default_style, set_column_size=False):
r = ws.row(row_pos) r = ws.row(row_pos)
for col, size, spec in row_data: for col, size, spec in row_data:
data = spec[4] data = spec[4]
formula = spec[5].get('formula') and xlwt.Formula(spec[5]['formula']) or None
formula = spec[5].get('formula') and \
xlwt.Formula(spec[5]['formula']) or None
style = spec[6] and spec[6] or row_style style = spec[6] and spec[6] or row_style
if not data: if not data:
# if no data, use default values # if no data, use default values
data = report_xls.xls_types_default[spec[3]] data = report_xls.xls_types_default[spec[3]]
if size != 1: if size != 1:
if formula: if formula:
ws.write_merge(row_pos, row_pos, col, col+size-1, data, style)
ws.write_merge(
row_pos, row_pos, col, col + size - 1, data, style)
else: else:
ws.write_merge(row_pos, row_pos, col, col+size-1, data, style)
ws.write_merge(
row_pos, row_pos, col, col + size - 1, data, style)
else: else:
if formula: if formula:
ws.write(row_pos, col, formula, style) ws.write(row_pos, col, formula, style)
@ -220,5 +244,5 @@ class report_xls(report_sxw):
if set_column_size: if set_column_size:
ws.col(col).width = spec[2] * 256 ws.col(col).width = spec[2] * 256
return row_pos + 1 return row_pos + 1
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

18
report_xls/utils.py

@ -3,7 +3,7 @@
# #
# OpenERP, Open Source Management Solution # OpenERP, Open Source Management Solution
# #
# Copyright (c) 2013 Noviat nv/sa (www.noviat.com). All rights reserved.
# Copyright (c) 2013-2014 Noviat nv/sa (www.noviat.com). All rights reserved.
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as # it under the terms of the GNU Affero General Public License as
@ -12,22 +12,24 @@
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details. # GNU Affero General Public License for more details.
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
#
def _render(code): def _render(code):
return compile(code, '<string>', 'eval')
return compile(code, '<string>', 'eval')
def rowcol_to_cell(row, col, row_abs=False, col_abs=False): def rowcol_to_cell(row, col, row_abs=False, col_abs=False):
# Code based upon utils from xlwt distribution # Code based upon utils from xlwt distribution
""" """
Convert numeric row/col notation to an Excel cell reference string in A1 notation.
Convert numeric row/col notation to an Excel cell
reference string in A1 notation.
""" """
d = col // 26 d = col // 26
m = col % 26 m = col % 26
@ -41,9 +43,9 @@ def rowcol_to_cell(row, col, row_abs=False, col_abs=False):
else: else:
col_abs = '' col_abs = ''
if d > 0: if d > 0:
chr1 = chr(ord('A') + d - 1)
chr1 = chr(ord('A') + d - 1)
chr2 = chr(ord('A') + m) chr2 = chr(ord('A') + m)
# Zero index to 1-index # Zero index to 1-index
return col_abs + chr1 + chr2 + row_abs + str(row + 1) return col_abs + chr1 + chr2 + row_abs + str(row + 1)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
Loading…
Cancel
Save