import datetime import io import json import operator import re from odoo import http from odoo.addons.web.controllers.main import ExportFormat, serialize_exception from odoo.exceptions import UserError from odoo.http import content_disposition, request from odoo.tools import pycompat from odoo.tools.misc import xlwt from odoo.tools.translate import _ class KsChartExport(ExportFormat, http.Controller): def base(self, data, token): params = json.loads(data) header,chart_data = operator.itemgetter('header','chart_data')(params) chart_data = json.loads(chart_data) chart_data['labels'].insert(0,'Measure') columns_headers = chart_data['labels'] import_data = [] for dataset in chart_data['datasets']: dataset['data'].insert(0, dataset['label']) import_data.append(dataset['data']) return request.make_response(self.from_data(columns_headers, import_data), headers=[('Content-Disposition', content_disposition(self.filename(header))), ('Content-Type', self.content_type)], cookies={'fileToken': token}) class KsChartExcelExport(KsChartExport, http.Controller): # Excel needs raw data to correctly handle numbers and date values raw_data = True @http.route('/ks_dashboard_ninja/export/chart_xls', type='http', auth="user") @serialize_exception def index(self, data, token): return self.base(data, token) @property def content_type(self): return 'application/vnd.ms-excel' def filename(self, base): return base + '.xls' def from_data(self, fields, rows): if len(rows) > 65535: raise UserError(_ ('There are too many rows (%s rows, limit: 65535) to export as Excel 97-2003 (.xls) format. Consider splitting the export.') % len (rows)) workbook = xlwt.Workbook() worksheet = workbook.add_sheet('Sheet 1') for i, fieldname in enumerate(fields): worksheet.write(0, i, fieldname) worksheet.col(i).width = 8000 # around 220 pixels base_style = xlwt.easyxf('align: wrap yes') date_style = xlwt.easyxf('align: wrap yes', num_format_str='YYYY-MM-DD') datetime_style = xlwt.easyxf('align: wrap yes', num_format_str='YYYY-MM-DD HH:mm:SS') for row_index, row in enumerate(rows): for cell_index, cell_value in enumerate(row): cell_style = base_style if isinstance(cell_value, bytes) and not isinstance(cell_value, pycompat.string_types): # because xls uses raw export, we can get a bytes object # here. xlwt does not support bytes values in Python 3 -> # assume this is base64 and decode to a string, if this # fails note that you can't export try: cell_value = pycompat.to_text(cell_value) except UnicodeDecodeError: raise UserError(_ ("Binary fields can not be exported to Excel unless their content is base64-encoded. That does not seem to be the case for %s.") % fields[cell_index]) if isinstance(cell_value, pycompat.string_types): cell_value = re.sub("\r", " ", pycompat.to_text(cell_value)) # Excel supports a maximum of 32767 characters in each cell: cell_value = cell_value[:32767] elif isinstance(cell_value, datetime.datetime): cell_style = datetime_style elif isinstance(cell_value, datetime.date): cell_style = date_style worksheet.write(row_index + 1, cell_index, cell_value, cell_style) fp = io.BytesIO() workbook.save(fp) fp.seek(0) data = fp.read() fp.close() return data class KsChartCsvExport(KsChartExport, http.Controller): @http.route('/ks_dashboard_ninja/export/chart_csv', type='http', auth="user") @serialize_exception def index(self, data, token): return self.base(data, token) @property def content_type(self): return 'text/csv;charset=utf8' def filename(self, base): return base + '.csv' def from_data(self, fields, rows): fp = io.BytesIO() writer = pycompat.csv_writer(fp, quoting=1) writer.writerow(fields) for data in rows: row = [] for d in data: # Spreadsheet apps tend to detect formulas on leading =, + and - if isinstance(d, pycompat.string_types) and d.startswith(('=', '-', '+')): d = "'" + d row.append(pycompat.to_text(d)) writer.writerow(row) return fp.getvalue()