diff options
Diffstat (limited to 'addons/web/controllers/pivot.py')
| -rw-r--r-- | addons/web/controllers/pivot.py | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/addons/web/controllers/pivot.py b/addons/web/controllers/pivot.py new file mode 100644 index 00000000..addb8f2b --- /dev/null +++ b/addons/web/controllers/pivot.py @@ -0,0 +1,110 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from collections import deque +import io +import json + +from odoo import http +from odoo.http import request +from odoo.tools import ustr +from odoo.tools.misc import xlsxwriter + + +class TableExporter(http.Controller): + + @http.route('/web/pivot/check_xlsxwriter', type='json', auth='none') + def check_xlsxwriter(self): + return xlsxwriter is not None + + @http.route('/web/pivot/export_xlsx', type='http', auth="user") + def export_xlsx(self, data, token): + jdata = json.loads(data) + output = io.BytesIO() + workbook = xlsxwriter.Workbook(output, {'in_memory': True}) + worksheet = workbook.add_worksheet(jdata['title']) + + header_bold = workbook.add_format({'bold': True, 'pattern': 1, 'bg_color': '#AAAAAA'}) + header_plain = workbook.add_format({'pattern': 1, 'bg_color': '#AAAAAA'}) + bold = workbook.add_format({'bold': True}) + + measure_count = jdata['measure_count'] + origin_count = jdata['origin_count'] + + # Step 1: writing col group headers + col_group_headers = jdata['col_group_headers'] + + # x,y: current coordinates + # carry: queue containing cell information when a cell has a >= 2 height + # and the drawing code needs to add empty cells below + x, y, carry = 1, 0, deque() + for i, header_row in enumerate(col_group_headers): + worksheet.write(i, 0, '', header_plain) + for header in header_row: + while (carry and carry[0]['x'] == x): + cell = carry.popleft() + for j in range(measure_count * (2 * origin_count - 1)): + worksheet.write(y, x+j, '', header_plain) + if cell['height'] > 1: + carry.append({'x': x, 'height': cell['height'] - 1}) + x = x + measure_count * (2 * origin_count - 1) + for j in range(header['width']): + worksheet.write(y, x + j, header['title'] if j == 0 else '', header_plain) + if header['height'] > 1: + carry.append({'x': x, 'height': header['height'] - 1}) + x = x + header['width'] + while (carry and carry[0]['x'] == x): + cell = carry.popleft() + for j in range(measure_count * (2 * origin_count - 1)): + worksheet.write(y, x+j, '', header_plain) + if cell['height'] > 1: + carry.append({'x': x, 'height': cell['height'] - 1}) + x = x + measure_count * (2 * origin_count - 1) + x, y = 1, y + 1 + + # Step 2: writing measure headers + measure_headers = jdata['measure_headers'] + + if measure_headers: + worksheet.write(y, 0, '', header_plain) + for measure in measure_headers: + style = header_bold if measure['is_bold'] else header_plain + worksheet.write(y, x, measure['title'], style) + for i in range(1, 2 * origin_count - 1): + worksheet.write(y, x+i, '', header_plain) + x = x + (2 * origin_count - 1) + x, y = 1, y + 1 + # set minimum width of cells to 16 which is around 88px + worksheet.set_column(0, len(measure_headers), 16) + + # Step 3: writing origin headers + origin_headers = jdata['origin_headers'] + + if origin_headers: + worksheet.write(y, 0, '', header_plain) + for origin in origin_headers: + style = header_bold if origin['is_bold'] else header_plain + worksheet.write(y, x, origin['title'], style) + x = x + 1 + y = y + 1 + + # Step 4: writing data + x = 0 + for row in jdata['rows']: + worksheet.write(y, x, row['indent'] * ' ' + ustr(row['title']), header_plain) + for cell in row['values']: + x = x + 1 + if cell.get('is_bold', False): + worksheet.write(y, x, cell['value'], bold) + else: + worksheet.write(y, x, cell['value']) + x, y = 0, y + 1 + + workbook.close() + xlsx_data = output.getvalue() + response = request.make_response(xlsx_data, + headers=[('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'), + ('Content-Disposition', 'attachment; filename=table.xlsx')], + cookies={'fileToken': token}) + + return response |
