'Export data as a Microsoft Excel spreadsheet
@param data_source: the source of the data that is to be encoded
as a spreadsheet, can be either of:
1) an S3Resource
2) an array of value dicts (dict of
column labels as first item, list of
field types as second item)
3) a dict like:
{columns: [key, ...],
headers: {key: label},
types: {key: type},
rows: [{key:value}],
@param title: the title for the output document
@param as_stream: return the buffer (StringIO) rather than
its contents (str), useful when the output
is supposed to be stored locally
@param attr: keyword parameters
@keyword title: the main title of the report
@keyword list_fields: fields to include in list views
@keyword report_groupby: used to create a grouping of the result:
either a Field object of the resource
or a string which matches a value in
the heading
@keyword use_colour: True to add colour to the cells, default False
@keyword evenodd: render different background colours
for even/odd rows ("stripes")'

def encode(self, data_source, title=None, as_stream=False, **attr):

    try:
        import xlwt
    except ImportError:
        error = self.ERROR.XLWT_ERROR
        current.log.error(error)
        raise HTTP(503, body=error)
    try:
        from xlrd.xldate import xldate_from_date_tuple, xldate_from_time_tuple, xldate_from_datetime_tuple
    except ImportError:
        error = self.ERROR.XLRD_ERROR
        current.log.error(error)
        raise HTTP(503, body=error)
    import datetime
    MAX_CELL_SIZE = self.MAX_CELL_SIZE
    COL_WIDTH_MULTIPLIER = self.COL_WIDTH_MULTIPLIER
    title = attr.get('title')
    if (title is None):
        title = current.T('Report')
    list_fields = attr.get('list_fields')
    group = attr.get('dt_group')
    use_colour = attr.get('use_colour', False)
    evenodd = attr.get('evenodd', True)
    if isinstance(data_source, dict):
        headers = data_source.get('headers', {})
        lfields = data_source.get('columns', list_fields)
        column_types = data_source.get('types')
        types = [column_types[col] for col in lfields]
        rows = data_source.get('rows')
    elif isinstance(data_source, (list, tuple)):
        headers = data_source[0]
        types = data_source[1]
        rows = data_source[2:]
        lfields = list_fields
    else:
        if (not list_fields):
            list_fields = data_source.list_fields()
        (title, types, lfields, headers, rows) = self.extract(data_source, list_fields)
    request = current.request
    if ((len(rows) > 0) and (len(lfields) > len(rows[0]))):
        msg = ("modules/s3/codecs/xls:    There    is    an    error    in    the    list    items,    a    field    doesn't    exist\nrequesting    url    %s\nHeaders    =    %d,    Data    Items    =    %d\nHeaders                    %s\nList    Fields    %s" % (request.url, len(lfields), len(rows[0]), headers, lfields))
        current.log.error(msg)
    report_groupby = (lfields[group] if group else None)
    groupby_label = (headers[report_groupby] if report_groupby else None)
    settings = current.deployment_settings
    date_format = settings.get_L10n_date_format()
    date_format_str = str(date_format)
    dt_format_translate = self.dt_format_translate
    date_format = dt_format_translate(date_format)
    time_format = dt_format_translate(settings.get_L10n_time_format())
    datetime_format = dt_format_translate(settings.get_L10n_datetime_format())
    title_row = settings.get_xls_title_row()
    styles = self._styles(use_colour=use_colour, evenodd=evenodd, datetime_format=datetime_format)
    book = xlwt.Workbook(encoding='utf-8')
    sheets = []
    row_limit = 65536
    sheetnum = (len(rows) / row_limit)
    sheet_name = str(title.replace('/', '    '))
    if (len(sheet_name) > 31):
        sheet_name = (sheet_name[:31] if (sheetnum == 1) else sheet_name[:28])
    count = 1
    while (len(sheets) <= sheetnum):
        sheets.append(book.add_sheet(('%s-%s' % (sheet_name, count))))
        count += 1
    if callable(title_row):
        title_row_length = title_row(None)
    else:
        title_row_length = 2
    header_style = styles['header']
    for sheet in sheets:
        if title_row:
            header_row = sheet.row(title_row_length)
        else:
            header_row = sheet.row(0)
        column_widths = []
        has_id = False
        col_index = 0
        for selector in lfields:
            if (selector == report_groupby):
                continue
            label = headers[selector]
            if (label == 'Id'):
                has_id = True
                column_widths.append(0)
                col_index += 1
                continue
            if (label == 'Sort'):
                continue
            if has_id:
                write_col_index = (col_index - 1)
            else:
                write_col_index = col_index
            header_row.write(write_col_index, str(label), header_style)
            width = max((len(label) * COL_WIDTH_MULTIPLIER), 2000)
            width = min(width, 65535)
            column_widths.append(width)
            sheet.col(write_col_index).width = width
            col_index += 1
    title = s3_str(title)
    if title_row:
        T = current.T
        large_header_style = styles['large_header']
        notes_style = styles['notes']
        for sheet in sheets:
            if callable(title_row):
                title_row(sheet)
            else:
                current_row = sheet.row(0)
                if (col_index > 0):
                    sheet.write_merge(0, 0, 0, col_index, title, large_header_style)
                current_row.height = 500
                current_row = sheet.row(1)
                current_row.write(0, ('%s:' % T('Date    Exported')), notes_style)
                current_row.write(1, request.now, notes_style)
                if ((16 * COL_WIDTH_MULTIPLIER) > width):
                    sheet.col(col_index).width = (16 * COL_WIDTH_MULTIPLIER)
    totalCols = col_index
    if title_row:
        row_index = title_row_length
    else:
        row_index = 0
    def get_current_row(row_count, row_limit):
        sheet_count = int((row_count / row_limit))
        row_number = (row_count - (sheet_count * row_limit))
        if (sheet_count > 0):
            row_number += 1
        return (sheets[sheet_count], sheets[sheet_count].row(row_number))
    subheading = None
    odd_style = styles['odd']
    even_style = styles['even']
    subheader_style = styles['subheader']
    for row in rows:
        row_index += 1
        (current_sheet, current_row) = get_current_row(row_index, row_limit)
        style = (even_style if ((row_index % 2) == 0) else odd_style)
        if report_groupby:
            represent = s3_strip_markup(s3_unicode(row[report_groupby]))
            if (subheading != represent):
                subheading = represent
                current_sheet.write_merge(row_index, row_index, 0, totalCols, subheading, subheader_style)
                row_index += 1
                (current_sheet, current_row) = get_current_row(row_index, row_limit)
                style = (even_style if ((row_index % 2) == 0) else odd_style)
        col_index = 0
        remaining_fields = lfields
        row_style = None
        if ('_style' in row):
            stylename = row['_style']
            if (stylename in styles):
                row_style = styles[stylename]
        if ('_group' in row):
            group_info = row['_group']
            label = group_info.get('label')
            totals = group_info.get('totals')
            if label:
                label = s3_strip_markup(s3_unicode(label))
                style = (row_style or subheader_style)
                span = group_info.get('span')
                if (span == 0):
                    current_sheet.write_merge(row_index, row_index, 0, (totalCols - 1), label, style)
                    if totals:
                        row_index += 1
                        (current_sheet, current_row) = get_current_row(row_index, row_limit)
                else:
                    current_sheet.write_merge(row_index, row_index, 0, (span - 1), label, style)
                    col_index = span
                    remaining_fields = lfields[span:]
            if (not totals):
                continue
        for field in remaining_fields:
            label = headers[field]
            if (label == groupby_label):
                continue
            if (label == 'Id'):
                col_index += 1
                continue
            if (field not in row):
                represent = ''
            else:
                represent = s3_strip_markup(s3_unicode(row[field]))
            coltype = types[col_index]
            if (coltype == 'sort'):
                continue
            if (len(represent) > MAX_CELL_SIZE):
                represent = represent[:MAX_CELL_SIZE]
            value = represent
            if (coltype == 'date'):
                try:
                    cell_datetime = datetime.datetime.strptime(value, date_format_str)
                    date_tuple = (cell_datetime.year, cell_datetime.month, cell_datetime.day)
                    value = xldate_from_date_tuple(date_tuple, 0)
                    style.num_format_str = date_format
                except:
                    pass
            elif (coltype == 'datetime'):
                try:
                    cell_datetime = datetime.datetime.strptime(value, date_format_str)
                    date_tuple = (cell_datetime.year, cell_datetime.month, cell_datetime.day, cell_datetime.hour, cell_datetime.minute, cell_datetime.second)
                    value = xldate_from_datetime_tuple(date_tuple, 0)
                    style.num_format_str = datetime_format
                except:
                    pass
            elif (coltype == 'time'):
                try:
                    cell_datetime = datetime.datetime.strptime(value, date_format_str)
                    date_tuple = (cell_datetime.hour, cell_datetime.minute, cell_datetime.second)
                    value = xldate_from_time_tuple(date_tuple)
                    style.num_format_str = time_format
                except:
                    pass
            elif (coltype == 'integer'):
                try:
                    value = int(value)
                    style.num_format_str = '0'
                except:
                    pass
            elif (coltype == 'double'):
                try:
                    value = float(value)
                    style.num_format_str = '0.00'
                except:
                    pass
            if has_id:
                write_col_index = (col_index - 1)
            else:
                write_col_index = col_index
            current_row.write(write_col_index, value, style)
            width = (len(represent) * COL_WIDTH_MULTIPLIER)
            if (width > column_widths[col_index]):
                column_widths[col_index] = width
                current_sheet.col(write_col_index).width = width
            col_index += 1
    for sheet in sheets:
        sheet.panes_frozen = True
        sheet.horz_split_pos = 1
    output = StringIO()
    book.save(output)
    output.seek(0)
    if as_stream:
        return output
    filename = ('%s_%s.xls' % (request.env.server_name, title))
    disposition = ('attachment;    filename="%s"' % filename)
    response = current.response
    response.headers['Content-Type'] = contenttype('.xls')
    response.headers['Content-disposition'] = disposition
    return output.read()
