Skip to main content

xlsxwriter-tables

Easily export nested data to Excel

This class is intended to be used with XlsxWriter. It serves several purposes:

  1. Co-location of column info and data-generation logic
  2. Easily specify deeply nested data as the source for column data
  3. Reference column headers dynamically in column formulas

The example.py file shows basic usage; excel_table.py is also thoroughly documented. I intend to document more examples of usage in the future.

API

Sample data
``` py
serialized_data = [
    {
        'alpha': {
            'oscar': True,
            'papa': {
                'romeo': State(
                    name='Alabama', 
                    statehood_granted=date(1819, 12, 14),
                    symbols={
                        'bird': 'Yellowhammer',
                        'flower': 'Camellia',
                    },
                ),
                'sierra': State(
                    name='Georgia', 
                    statehood_granted=date(1788, 1, 2),
                    symbols={
                        'bird': 'Brown Thrasher',
                        'flower': 'Cherokee Rose',
                    },
                ),
            }
        },
        'bravo': 22,
        'charlie': 4,
    },
    {
        'alpha': {
            'oscar': False,
            'papa': {
                'romeo': State(
                    name='Minnesota', 
                    statehood_granted=date(1858, 5, 11),
                    symbols={
                        'bird': 'Common Loon',
                        'flower': 'Ladys Slipper',
                    },
                ),
                'sierra': State(
                    name='Wisconsin', 
                    statehood_granted=date(1848, 5, 29),
                    symbols={
                        'bird': 'Robin',
                        'flower': 'Wood Violet',
                    },
                ),
            }
        },
        'bravo': 32,
        'charlie': 30,
    },
    {
        'alpha': {
            'oscar': None,
            'papa': {
                'romeo': State(
                    name='Maryland', 
                    statehood_granted=date(1776, 7, 4),
                    symbols={
                        'bird': 'Baltimore Oriole',
                        'flower': 'Black-Eyed Susan',
                    },
                ),
                'sierra': State(
                    name='Virginia', 
                    statehood_granted=date(1788, 6, 25),
                    symbols={
                        'bird': 'Cardinal',
                        'flower': 'Flowering Dogwood',
                    },
                ),
            }
        },
        'bravo': 7,
        'charlie': 10,
    },
]
```

Given the sample data above, we can generate an Excel table for export using XlsxWriter with the following code:

excel_table = ExcelTable(

    data=serialized_data,

    columns=dict(
        oscar='alpha.oscar',
        state_name='alpha.papa.romeo.name',
        statehood_granted='alpha.papa.romeo.statehood_granted',
        state_bird='alpha.papa.romeo.symbols.bird',
        state_flower='alpha.papa.romeo.symbols.flower',
        other_states_bird='alpha.papa.sierra.symbols.bird',
        bravo=None,
        charlie=None,
        average_bravo_charlie=dict(
            header='Avg of Bravo/Charlie',
            data_accessor=lambda item: None,
            formula='=AVERAGE({bravo}, {charlie})',
        ),
    )
)

Co-location of Column and Data Accessor Code

One advantage to this approach is that everything pertaining to a single column is in one place! The alternative approach force columns to be specified in one place, and data generated in another. This means that to make a change to a column or columns, you have to change it in multiple places. As the size of your tables grow, it becomes more difficult to maintain.

With this style, columns are defined with the logic used to generate the data in each row for that column. If columns need to be reordered, headers renamed, or additional info updated, there is only one place that these changes need to be made.

Easily Flatten Nested Data Into Row

The next advantage is that it provides a concise, readable style for accessing nested data by chaining properties together (dot-syntax is the default, but custom separators can be specified). This makes it much easier to spot inconsistencies across multiple columns, and identify why a cell is showing up blank or generating an AttributeError.

Unbreakable Formula References

With XlsxWriter, column formulas can be defined with references to other columns in the table (formula='=SUM([@Qty] * [@Price]). Hard-coding column headers is a bad idea, however, as this is subject to break if the referenced column header changes. In this case, it can be difficult to notice that a formula broke, especially in large Excel tables.

xlsxwriter-tables solves this by using the columns' keys to generate dynamic references to each column in column formulas. This approach will fail at runtime if the keys change, alerting you that a change needs to be made.

Nesting Classes and Dicts

The class is flexible enough to handle both class instances and dicts. Classes can be nested inside of dicts (romeo is an instance of State in the example). Likewise, dicts can be properties of class instances (symbols, a dict, is a property of each State instance). The same syntax is used to access nested values of both classes and dicts.

Field Separator Syntax

The default field separator is the dot (.), but custom characters can be specified. For instance, to assimilate Django's ORM-style "dunder" syntax for querying fields, use separator='__'. Columns would then use this like so:

...
separator='__',
columns=dict(
    oscar='alpha__oscar',
    state_name='alpha__papa__romeo__name',
    ...

Specifying column=None

In the simplest cases where the column key is also the top-level attribute that is desired, and the column key is the desired column header, set the column's value to None.

For example, these column definitions will generate the header, 'Bravo', and access data on each item using item.bravo (class) or item['bravo'] (dict):

bravo=None,
bravo={},
bravo=(),

AttributeErrors and KeyErrors

By default, if attributes or keys cannot be found, they fail gracefully - meaning they return the value None, and cell values for those fields are blank. This is useful for cases where the shape of each item is not expected to conform perfectly to the column schema.

For debugging purposes, or in other cases where the data is expected to be uniform, you can set raise_attribute_errors=True:

excel_table = ExcelTable(
    ...
    raise_attribute_errors=True,

    columns=dict(
        alpha_quebec='alpha.quebec',
        ...
    )
)

This will result in:

image

Other Exceptions

Any other error is printed to the cell in which it occurred, to help diagnose.

Column Header Text

The header attribute defaults to the title-cased dictionary key, unless a header is explicitly provided. For example:

THIS COLUMN                                 BECOMES THIS HEADER
-------------------------------------------------------------------
oscar=...,                              --> 'Oscar'
state_name=...,                         --> 'State Name'

average_bravo_charlie=dict(             --> 'Avg of Bravo/Charlie'
    header='Avg of Bravo/Charlie',
    ...
)

Additional Column Attributes

Column attributes can be supplied in each column's dictionary, following XlsxWriter's docs. With the exception of formula, these attributes simply get passed through to XlsxWriter.

Column Formulas

Formulas can be specified per XlsxWriter's docs. To dynamically reference the calculated column header of another column in a formula, use curly braces and the column's kwarg.

For instance, the following code for average_bravo_charlie...

bravo=None,
charlie=None,
average_bravo_charlie=dict(
    ...
    formula='=AVERAGE({bravo}, {charlie})',
),

...will generate this column formula:

'=AVERAGE([@[Bravo]], [@[Charlie]])

This means that changing the header text in a referenced column will not break the formula! Further, changing the column's kwarg will break the formula if it is not also updated. However, it will raise an error at runtime, rather than failing silently in the Excel file.

Saving to Excel

To save the data to an Excel file, use XlsxWriter's worksheet.add_table() method as usual.

The ExcelTable class automatically calculates the top, left, bottom, and right coordinates of the table based on the size of the data. These values are available in excel_table.coordinates as a tuple, making it easy to spread them into the add_table() call.

import xlsxwriter

workbook = xlsxwriter.Workbook('example.xlsx')

worksheet = workbook.add_worksheet()

excel_table = ExcelTable(...)

worksheet.add_table(
    *excel_table.coordinates,
    {
        'columns': excel_table.columns,
        'data': excel_table.data,
        'total_row': excel_table.include_total_row,
        ...
    }
)

workbook.close()

Metadata

Release files for xlsxwriter-tables 0.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for xlsxwriter-tables 0.0.1
File Size Uploaded Provenance Actions
xlsxwriter-tables-0.0.1.tar.gz 8.2 kB Without provenance Details for xlsxwriter-tables-0.0.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for xlsxwriter-tables 0.0.1
File Interpreter ABI Platform Actions
xlsxwriter_tables-0.0.1-py3-none-any.whl Python 3 none any Details for xlsxwriter_tables-0.0.1-py3-none-any.whl

Total release size: 16.1 kB

Release files / xlsxwriter-tables-0.0.1.tar.gz

Download URL xlsxwriter-tables-0.0.1.tar.gz
Size 8.2 kB
Tags Source
SHA-256 checksum
How to use checksums
4e62be129a77670ff1036a5151e6f16798098a391a9ba8bb41563fe02ce6b108
BLAKE2b-256 checksum
How to use checksums
7fa2bf179a33f997bc8fbd871b2a20b6501c2b20ac4df14703894887ade04bbd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.7.1 importlib_metadata/4.10.0 pkginfo/1.8.2 requests/2.27.1 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.9.0

Release files / xlsxwriter_tables-0.0.1-py3-none-any.whl

Download URL xlsxwriter_tables-0.0.1-py3-none-any.whl
Size 7.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7d14233825e8b5b69bb202e497e804bd8c520d0c4c390666a8432d0508b85482
BLAKE2b-256 checksum
How to use checksums
7815420a9388982c94b7d653ae49c1d2168fea41a0fac951dd1ec41dff9b32cb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.7.1 importlib_metadata/4.10.0 pkginfo/1.8.2 requests/2.27.1 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.9.0

Security for xlsxwriter-tables 0.0.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.0.1 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page