> ## Documentation Index
> Fetch the complete documentation index at: https://docs.activeviam.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Generating PDF reports

<a id="module-docs.pages.generating_pdf_reports" />

The Python ecosystem has everything needed to create PDF reports with a custom layout.

This guide shows how to generate a PDF report extracting data from an Atoti session and displaying it as a table spanning over multiple pages and a chart.

## Creating the session

Let’s start by creating an Atoti session and loading some data into it:

```pycon theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
>>> import atoti as tt
>>> session = tt.Session.start()  
```

```pycon theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
>>> sales_table = session.read_csv(
...     data_directory / "sales.csv",
...     keys={"Sale ID"},
...     table_name="Sales",
... )
>>> products_table = session.read_csv(
...     data_directory / "products.csv",
...     keys={"Product"},
...     table_name="Products",
... )
>>> sales_table.join(
...     products_table, sales_table["Product"] == products_table["Product"]
... )
>>> cube = session.create_cube(sales_table)
>>> l, m = cube.levels, cube.measures
```

## Querying the session

The first query will retrieve the values of a few measures for each **Sub category** and **Brand**.

### Styling tables

The result of this first query will be displayed as a table in our report.
We’ll use three different techniques to style it:

#### Defining cell properties in the MDX query

In the query below, we use calculated measures to style the table cells.
In particular, `FORE_COLOR=IIf([Measures].[Count] < 300, RGB(255, 135, 135), NULL)` in the following query ensures that the cells for the **contributors.COUNT** measure are colored red if their value is less than 300.

```pycon theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
>>> mdx = '''
...     WITH
...     MEMBER [Measures].[Count] as [Measures].[contributors.COUNT],
...     FORE_COLOR=IIf([Measures].[Count] < 300, RGB(255, 135, 135), NULL)
...     MEMBER [Measures].[Quantity] as [Measures].[Quantity.SUM],
...     FORE_COLOR=IIf([Measures].[Quantity.SUM] > 700, RGB(50, 150, 50), NULL)
...     SELECT
...       NON EMPTY Crossjoin(
...         Hierarchize(
...           Descendants(
...             {
...               [Products].[Sub category].[AllMember]
...             },
...             1,
...             SELF_AND_BEFORE
...           )
...         ),
...         Hierarchize(
...           Descendants(
...             {
...               [Products].[Brand].[AllMember]
...             },
...             1,
...             SELF_AND_BEFORE
...           )
...         )
...       ) ON ROWS,
...       NON EMPTY {
...         [Measures].[Count],
...         [Measures].[Quantity],
...         [Measures].[Unit price.MEAN],
...         [Measures].[Unit price.SUM]
...       } ON COLUMNS
...     FROM [Sales]
...     CELL PROPERTIES
...       FORE_COLOR,
...       FORMATTED_VALUE,
...       VALUE
... '''
>>> df = session.query_mdx(mdx, keep_totals=True)
>>> df.style  
```

<iframe srcdoc="<style type=&#x22;text/css&#x22;>#T__row0_col1, #T__row1_col1, #T__row2_col1, #T__row3_col1, #T__row7_col1, #T__row12_col1, #T__row17_col1, #T__row22_col1 {color: rgb(50, 150, 50);}#T__row4_col0, #T__row5_col0, #T__row6_col0, #T__row8_col0, #T__row9_col0, #T__row10_col0, #T__row11_col0, #T__row16_col0, #T__row21_col0, #T__row23_col0, #T__row24_col0 {color: rgb(255, 135, 135);}</style><table id=&#x22;T_&#x22;><thead><tr><th class=&#x22;blank&#x22; > </th><th class=&#x22;blank level0&#x22; > </th><th id=&#x22;T__level0_col0&#x22; class=&#x22;col_heading level0 col0&#x22; >Count</th><th id=&#x22;T__level0_col1&#x22; class=&#x22;col_heading level0 col1&#x22; >Quantity</th><th id=&#x22;T__level0_col2&#x22; class=&#x22;col_heading level0 col2&#x22; >Unit price.MEAN</th><th id=&#x22;T__level0_col3&#x22; class=&#x22;col_heading level0 col3&#x22; >Unit price.SUM</th></tr><tr><th class=&#x22;index_name level0&#x22; >Sub category</th><th class=&#x22;index_name level1&#x22; >Brand</th><th class=&#x22;blank col0&#x22; > </th><th class=&#x22;blank col1&#x22; > </th><th class=&#x22;blank col2&#x22; > </th><th class=&#x22;blank col3&#x22; > </th></tr></thead><tbody><tr><th id=&#x22;T__level0_row0&#x22; class=&#x22;row_heading level0 row0&#x22; >Total</th><th id=&#x22;T__level1_row0&#x22; class=&#x22;row_heading level1 row0&#x22; ></th><td id=&#x22;T__row0_col0&#x22; class=&#x22;data row0 col0&#x22; >5000</td><td id=&#x22;T__row0_col1&#x22; class=&#x22;data row0 col1&#x22; >8077.0</td><td id=&#x22;T__row0_col2&#x22; class=&#x22;data row0 col2&#x22; >119.38</td><td id=&#x22;T__row0_col3&#x22; class=&#x22;data row0 col3&#x22; >596,922.00</td></tr><tr><th id=&#x22;T__level0_row1&#x22; class=&#x22;row_heading level0 row1&#x22; rowspan=&#x22;3&#x22;>Bed</th><th id=&#x22;T__level1_row1&#x22; class=&#x22;row_heading level1 row1&#x22; ></th><td id=&#x22;T__row1_col0&#x22; class=&#x22;data row1 col0&#x22; >984</td><td id=&#x22;T__row1_col1&#x22; class=&#x22;data row1 col1&#x22; >1588.0</td><td id=&#x22;T__row1_col2&#x22; class=&#x22;data row1 col2&#x22; >321.25</td><td id=&#x22;T__row1_col3&#x22; class=&#x22;data row1 col3&#x22; >316,110.00</td></tr><tr><th id=&#x22;T__level1_row2&#x22; class=&#x22;row_heading level1 row2&#x22; >Basic</th><td id=&#x22;T__row2_col0&#x22; class=&#x22;data row2 col0&#x22; >492</td><td id=&#x22;T__row2_col1&#x22; class=&#x22;data row2 col1&#x22; >793.0</td><td id=&#x22;T__row2_col2&#x22; class=&#x22;data row2 col2&#x22; >337.50</td><td id=&#x22;T__row2_col3&#x22; class=&#x22;data row2 col3&#x22; >166,050.00</td></tr><tr><th id=&#x22;T__level1_row3&#x22; class=&#x22;row_heading level1 row3&#x22; >Mega</th><td id=&#x22;T__row3_col0&#x22; class=&#x22;data row3 col0&#x22; >492</td><td id=&#x22;T__row3_col1&#x22; class=&#x22;data row3 col1&#x22; >795.0</td><td id=&#x22;T__row3_col2&#x22; class=&#x22;data row3 col2&#x22; >305.00</td><td id=&#x22;T__row3_col3&#x22; class=&#x22;data row3 col3&#x22; >150,060.00</td></tr><tr><th id=&#x22;T__level0_row4&#x22; class=&#x22;row_heading level0 row4&#x22; rowspan=&#x22;3&#x22;>Chair</th><th id=&#x22;T__level1_row4&#x22; class=&#x22;row_heading level1 row4&#x22; ></th><td id=&#x22;T__row4_col0&#x22; class=&#x22;data row4 col0&#x22; >246</td><td id=&#x22;T__row4_col1&#x22; class=&#x22;data row4 col1&#x22; >401.0</td><td id=&#x22;T__row4_col2&#x22; class=&#x22;data row4 col2&#x22; >60.00</td><td id=&#x22;T__row4_col3&#x22; class=&#x22;data row4 col3&#x22; >14,760.00</td></tr><tr><th id=&#x22;T__level1_row5&#x22; class=&#x22;row_heading level1 row5&#x22; >Basic</th><td id=&#x22;T__row5_col0&#x22; class=&#x22;data row5 col0&#x22; >164</td><td id=&#x22;T__row5_col1&#x22; class=&#x22;data row5 col1&#x22; >266.0</td><td id=&#x22;T__row5_col2&#x22; class=&#x22;data row5 col2&#x22; >60.00</td><td id=&#x22;T__row5_col3&#x22; class=&#x22;data row5 col3&#x22; >9,840.00</td></tr><tr><th id=&#x22;T__level1_row6&#x22; class=&#x22;row_heading level1 row6&#x22; >Mega</th><td id=&#x22;T__row6_col0&#x22; class=&#x22;data row6 col0&#x22; >82</td><td id=&#x22;T__row6_col1&#x22; class=&#x22;data row6 col1&#x22; >135.0</td><td id=&#x22;T__row6_col2&#x22; class=&#x22;data row6 col2&#x22; >60.00</td><td id=&#x22;T__row6_col3&#x22; class=&#x22;data row6 col3&#x22; >4,920.00</td></tr><tr><th id=&#x22;T__level0_row7&#x22; class=&#x22;row_heading level0 row7&#x22; rowspan=&#x22;5&#x22;>Hoodie</th><th id=&#x22;T__level1_row7&#x22; class=&#x22;row_heading level1 row7&#x22; ></th><td id=&#x22;T__row7_col0&#x22; class=&#x22;data row7 col0&#x22; >738</td><td id=&#x22;T__row7_col1&#x22; class=&#x22;data row7 col1&#x22; >1186.0</td><td id=&#x22;T__row7_col2&#x22; class=&#x22;data row7 col2&#x22; >47.33</td><td id=&#x22;T__row7_col3&#x22; class=&#x22;data row7 col3&#x22; >34,932.00</td></tr><tr><th id=&#x22;T__level1_row8&#x22; class=&#x22;row_heading level1 row8&#x22; >Basic</th><td id=&#x22;T__row8_col0&#x22; class=&#x22;data row8 col0&#x22; >164</td><td id=&#x22;T__row8_col1&#x22; class=&#x22;data row8 col1&#x22; >264.0</td><td id=&#x22;T__row8_col2&#x22; class=&#x22;data row8 col2&#x22; >47.00</td><td id=&#x22;T__row8_col3&#x22; class=&#x22;data row8 col3&#x22; >7,708.00</td></tr><tr><th id=&#x22;T__level1_row9&#x22; class=&#x22;row_heading level1 row9&#x22; >Mega</th><td id=&#x22;T__row9_col0&#x22; class=&#x22;data row9 col0&#x22; >164</td><td id=&#x22;T__row9_col1&#x22; class=&#x22;data row9 col1&#x22; >261.0</td><td id=&#x22;T__row9_col2&#x22; class=&#x22;data row9 col2&#x22; >46.50</td><td id=&#x22;T__row9_col3&#x22; class=&#x22;data row9 col3&#x22; >7,626.00</td></tr><tr><th id=&#x22;T__level1_row10&#x22; class=&#x22;row_heading level1 row10&#x22; >NewBrand</th><td id=&#x22;T__row10_col0&#x22; class=&#x22;data row10 col0&#x22; >164</td><td id=&#x22;T__row10_col1&#x22; class=&#x22;data row10 col1&#x22; >262.0</td><td id=&#x22;T__row10_col2&#x22; class=&#x22;data row10 col2&#x22; >48.50</td><td id=&#x22;T__row10_col3&#x22; class=&#x22;data row10 col3&#x22; >7,954.00</td></tr><tr><th id=&#x22;T__level1_row11&#x22; class=&#x22;row_heading level1 row11&#x22; >Over</th><td id=&#x22;T__row11_col0&#x22; class=&#x22;data row11 col0&#x22; >246</td><td id=&#x22;T__row11_col1&#x22; class=&#x22;data row11 col1&#x22; >399.0</td><td id=&#x22;T__row11_col2&#x22; class=&#x22;data row11 col2&#x22; >47.33</td><td id=&#x22;T__row11_col3&#x22; class=&#x22;data row11 col3&#x22; >11,644.00</td></tr><tr><th id=&#x22;T__level0_row12&#x22; class=&#x22;row_heading level0 row12&#x22; rowspan=&#x22;5&#x22;>Shoes</th><th id=&#x22;T__level1_row12&#x22; class=&#x22;row_heading level1 row12&#x22; ></th><td id=&#x22;T__row12_col0&#x22; class=&#x22;data row12 col0&#x22; >1310</td><td id=&#x22;T__row12_col1&#x22; class=&#x22;data row12 col1&#x22; >2123.0</td><td id=&#x22;T__row12_col2&#x22; class=&#x22;data row12 col2&#x22; >60.00</td><td id=&#x22;T__row12_col3&#x22; class=&#x22;data row12 col3&#x22; >78,600.00</td></tr><tr><th id=&#x22;T__level1_row13&#x22; class=&#x22;row_heading level1 row13&#x22; >Basic</th><td id=&#x22;T__row13_col0&#x22; class=&#x22;data row13 col0&#x22; >409</td><td id=&#x22;T__row13_col1&#x22; class=&#x22;data row13 col1&#x22; >661.0</td><td id=&#x22;T__row13_col2&#x22; class=&#x22;data row13 col2&#x22; >60.00</td><td id=&#x22;T__row13_col3&#x22; class=&#x22;data row13 col3&#x22; >24,540.00</td></tr><tr><th id=&#x22;T__level1_row14&#x22; class=&#x22;row_heading level1 row14&#x22; >Mega</th><td id=&#x22;T__row14_col0&#x22; class=&#x22;data row14 col0&#x22; >328</td><td id=&#x22;T__row14_col1&#x22; class=&#x22;data row14 col1&#x22; >532.0</td><td id=&#x22;T__row14_col2&#x22; class=&#x22;data row14 col2&#x22; >60.00</td><td id=&#x22;T__row14_col3&#x22; class=&#x22;data row14 col3&#x22; >19,680.00</td></tr><tr><th id=&#x22;T__level1_row15&#x22; class=&#x22;row_heading level1 row15&#x22; >NewBrand</th><td id=&#x22;T__row15_col0&#x22; class=&#x22;data row15 col0&#x22; >327</td><td id=&#x22;T__row15_col1&#x22; class=&#x22;data row15 col1&#x22; >531.0</td><td id=&#x22;T__row15_col2&#x22; class=&#x22;data row15 col2&#x22; >60.00</td><td id=&#x22;T__row15_col3&#x22; class=&#x22;data row15 col3&#x22; >19,620.00</td></tr><tr><th id=&#x22;T__level1_row16&#x22; class=&#x22;row_heading level1 row16&#x22; >Over</th><td id=&#x22;T__row16_col0&#x22; class=&#x22;data row16 col0&#x22; >246</td><td id=&#x22;T__row16_col1&#x22; class=&#x22;data row16 col1&#x22; >399.0</td><td id=&#x22;T__row16_col2&#x22; class=&#x22;data row16 col2&#x22; >60.00</td><td id=&#x22;T__row16_col3&#x22; class=&#x22;data row16 col3&#x22; >14,760.00</td></tr><tr><th id=&#x22;T__level0_row17&#x22; class=&#x22;row_heading level0 row17&#x22; rowspan=&#x22;5&#x22;>T-shirt</th><th id=&#x22;T__level1_row17&#x22; class=&#x22;row_heading level1 row17&#x22; ></th><td id=&#x22;T__row17_col0&#x22; class=&#x22;data row17 col0&#x22; >1230</td><td id=&#x22;T__row17_col1&#x22; class=&#x22;data row17 col1&#x22; >1991.0</td><td id=&#x22;T__row17_col2&#x22; class=&#x22;data row17 col2&#x22; >22.00</td><td id=&#x22;T__row17_col3&#x22; class=&#x22;data row17 col3&#x22; >27,060.00</td></tr><tr><th id=&#x22;T__level1_row18&#x22; class=&#x22;row_heading level1 row18&#x22; >Basic</th><td id=&#x22;T__row18_col0&#x22; class=&#x22;data row18 col0&#x22; >328</td><td id=&#x22;T__row18_col1&#x22; class=&#x22;data row18 col1&#x22; >533.0</td><td id=&#x22;T__row18_col2&#x22; class=&#x22;data row18 col2&#x22; >22.00</td><td id=&#x22;T__row18_col3&#x22; class=&#x22;data row18 col3&#x22; >7,216.00</td></tr><tr><th id=&#x22;T__level1_row19&#x22; class=&#x22;row_heading level1 row19&#x22; >Mega</th><td id=&#x22;T__row19_col0&#x22; class=&#x22;data row19 col0&#x22; >328</td><td id=&#x22;T__row19_col1&#x22; class=&#x22;data row19 col1&#x22; >529.0</td><td id=&#x22;T__row19_col2&#x22; class=&#x22;data row19 col2&#x22; >21.50</td><td id=&#x22;T__row19_col3&#x22; class=&#x22;data row19 col3&#x22; >7,052.00</td></tr><tr><th id=&#x22;T__level1_row20&#x22; class=&#x22;row_heading level1 row20&#x22; >NewBrand</th><td id=&#x22;T__row20_col0&#x22; class=&#x22;data row20 col0&#x22; >328</td><td id=&#x22;T__row20_col1&#x22; class=&#x22;data row20 col1&#x22; >528.0</td><td id=&#x22;T__row20_col2&#x22; class=&#x22;data row20 col2&#x22; >22.50</td><td id=&#x22;T__row20_col3&#x22; class=&#x22;data row20 col3&#x22; >7,380.00</td></tr><tr><th id=&#x22;T__level1_row21&#x22; class=&#x22;row_heading level1 row21&#x22; >Over</th><td id=&#x22;T__row21_col0&#x22; class=&#x22;data row21 col0&#x22; >246</td><td id=&#x22;T__row21_col1&#x22; class=&#x22;data row21 col1&#x22; >401.0</td><td id=&#x22;T__row21_col2&#x22; class=&#x22;data row21 col2&#x22; >22.00</td><td id=&#x22;T__row21_col3&#x22; class=&#x22;data row21 col3&#x22; >5,412.00</td></tr><tr><th id=&#x22;T__level0_row22&#x22; class=&#x22;row_heading level0 row22&#x22; rowspan=&#x22;3&#x22;>Table</th><th id=&#x22;T__level1_row22&#x22; class=&#x22;row_heading level1 row22&#x22; ></th><td id=&#x22;T__row22_col0&#x22; class=&#x22;data row22 col0&#x22; >492</td><td id=&#x22;T__row22_col1&#x22; class=&#x22;data row22 col1&#x22; >788.0</td><td id=&#x22;T__row22_col2&#x22; class=&#x22;data row22 col2&#x22; >255.00</td><td id=&#x22;T__row22_col3&#x22; class=&#x22;data row22 col3&#x22; >125,460.00</td></tr><tr><th id=&#x22;T__level1_row23&#x22; class=&#x22;row_heading level1 row23&#x22; >Basic</th><td id=&#x22;T__row23_col0&#x22; class=&#x22;data row23 col0&#x22; >246</td><td id=&#x22;T__row23_col1&#x22; class=&#x22;data row23 col1&#x22; >392.0</td><td id=&#x22;T__row23_col2&#x22; class=&#x22;data row23 col2&#x22; >240.00</td><td id=&#x22;T__row23_col3&#x22; class=&#x22;data row23 col3&#x22; >59,040.00</td></tr><tr><th id=&#x22;T__level1_row24&#x22; class=&#x22;row_heading level1 row24&#x22; >Mega</th><td id=&#x22;T__row24_col0&#x22; class=&#x22;data row24 col0&#x22; >246</td><td id=&#x22;T__row24_col1&#x22; class=&#x22;data row24 col1&#x22; >396.0</td><td id=&#x22;T__row24_col2&#x22; class=&#x22;data row24 col2&#x22; >270.00</td><td id=&#x22;T__row24_col3&#x22; class=&#x22;data row24 col3&#x22; >66,420.00</td></tr></tbody></table>" width="100%" height="500" frameborder="0" />

#### Applying style with pandas

[pandas provides many ways to style a DataFrame](https://pandas.pydata.org/pandas-docs/version/2.0/user_guide/style.html).

Here, we’ll apply a background color to a specified row:

```pycon theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
>>> index_slice = pd.IndexSlice
>>> subset = index_slice[index_slice["Chair", "Mega"], :]
>>> df_style = df.style.set_properties(
...     **{"background-color": "#ffff92"},
...     subset=subset,
... )
>>> df_style  
```

<iframe srcdoc="<style type=&#x22;text/css&#x22;>#T__row0_col1, #T__row1_col1, #T__row2_col1, #T__row3_col1, #T__row7_col1, #T__row12_col1, #T__row17_col1, #T__row22_col1 {color: rgb(50, 150, 50);}#T__row4_col0, #T__row5_col0, #T__row8_col0, #T__row9_col0, #T__row10_col0, #T__row11_col0, #T__row16_col0, #T__row21_col0, #T__row23_col0, #T__row24_col0 {color: rgb(255, 135, 135);}#T__row6_col0 {color: rgb(255, 135, 135);background-color: #ffff92;}#T__row6_col1, #T__row6_col2, #T__row6_col3 {background-color: #ffff92;}</style><table id=&#x22;T_&#x22;><thead><tr><th class=&#x22;blank&#x22; > </th><th class=&#x22;blank level0&#x22; > </th><th id=&#x22;T__level0_col0&#x22; class=&#x22;col_heading level0 col0&#x22; >Count</th><th id=&#x22;T__level0_col1&#x22; class=&#x22;col_heading level0 col1&#x22; >Quantity</th><th id=&#x22;T__level0_col2&#x22; class=&#x22;col_heading level0 col2&#x22; >Unit price.MEAN</th><th id=&#x22;T__level0_col3&#x22; class=&#x22;col_heading level0 col3&#x22; >Unit price.SUM</th></tr><tr><th class=&#x22;index_name level0&#x22; >Sub category</th><th class=&#x22;index_name level1&#x22; >Brand</th><th class=&#x22;blank col0&#x22; > </th><th class=&#x22;blank col1&#x22; > </th><th class=&#x22;blank col2&#x22; > </th><th class=&#x22;blank col3&#x22; > </th></tr></thead><tbody><tr><th id=&#x22;T__level0_row0&#x22; class=&#x22;row_heading level0 row0&#x22; >Total</th><th id=&#x22;T__level1_row0&#x22; class=&#x22;row_heading level1 row0&#x22; ></th><td id=&#x22;T__row0_col0&#x22; class=&#x22;data row0 col0&#x22; >5000</td><td id=&#x22;T__row0_col1&#x22; class=&#x22;data row0 col1&#x22; >8077.0</td><td id=&#x22;T__row0_col2&#x22; class=&#x22;data row0 col2&#x22; >119.38</td><td id=&#x22;T__row0_col3&#x22; class=&#x22;data row0 col3&#x22; >596,922.00</td></tr><tr><th id=&#x22;T__level0_row1&#x22; class=&#x22;row_heading level0 row1&#x22; rowspan=&#x22;3&#x22;>Bed</th><th id=&#x22;T__level1_row1&#x22; class=&#x22;row_heading level1 row1&#x22; ></th><td id=&#x22;T__row1_col0&#x22; class=&#x22;data row1 col0&#x22; >984</td><td id=&#x22;T__row1_col1&#x22; class=&#x22;data row1 col1&#x22; >1588.0</td><td id=&#x22;T__row1_col2&#x22; class=&#x22;data row1 col2&#x22; >321.25</td><td id=&#x22;T__row1_col3&#x22; class=&#x22;data row1 col3&#x22; >316,110.00</td></tr><tr><th id=&#x22;T__level1_row2&#x22; class=&#x22;row_heading level1 row2&#x22; >Basic</th><td id=&#x22;T__row2_col0&#x22; class=&#x22;data row2 col0&#x22; >492</td><td id=&#x22;T__row2_col1&#x22; class=&#x22;data row2 col1&#x22; >793.0</td><td id=&#x22;T__row2_col2&#x22; class=&#x22;data row2 col2&#x22; >337.50</td><td id=&#x22;T__row2_col3&#x22; class=&#x22;data row2 col3&#x22; >166,050.00</td></tr><tr><th id=&#x22;T__level1_row3&#x22; class=&#x22;row_heading level1 row3&#x22; >Mega</th><td id=&#x22;T__row3_col0&#x22; class=&#x22;data row3 col0&#x22; >492</td><td id=&#x22;T__row3_col1&#x22; class=&#x22;data row3 col1&#x22; >795.0</td><td id=&#x22;T__row3_col2&#x22; class=&#x22;data row3 col2&#x22; >305.00</td><td id=&#x22;T__row3_col3&#x22; class=&#x22;data row3 col3&#x22; >150,060.00</td></tr><tr><th id=&#x22;T__level0_row4&#x22; class=&#x22;row_heading level0 row4&#x22; rowspan=&#x22;3&#x22;>Chair</th><th id=&#x22;T__level1_row4&#x22; class=&#x22;row_heading level1 row4&#x22; ></th><td id=&#x22;T__row4_col0&#x22; class=&#x22;data row4 col0&#x22; >246</td><td id=&#x22;T__row4_col1&#x22; class=&#x22;data row4 col1&#x22; >401.0</td><td id=&#x22;T__row4_col2&#x22; class=&#x22;data row4 col2&#x22; >60.00</td><td id=&#x22;T__row4_col3&#x22; class=&#x22;data row4 col3&#x22; >14,760.00</td></tr><tr><th id=&#x22;T__level1_row5&#x22; class=&#x22;row_heading level1 row5&#x22; >Basic</th><td id=&#x22;T__row5_col0&#x22; class=&#x22;data row5 col0&#x22; >164</td><td id=&#x22;T__row5_col1&#x22; class=&#x22;data row5 col1&#x22; >266.0</td><td id=&#x22;T__row5_col2&#x22; class=&#x22;data row5 col2&#x22; >60.00</td><td id=&#x22;T__row5_col3&#x22; class=&#x22;data row5 col3&#x22; >9,840.00</td></tr><tr><th id=&#x22;T__level1_row6&#x22; class=&#x22;row_heading level1 row6&#x22; >Mega</th><td id=&#x22;T__row6_col0&#x22; class=&#x22;data row6 col0&#x22; >82</td><td id=&#x22;T__row6_col1&#x22; class=&#x22;data row6 col1&#x22; >135.0</td><td id=&#x22;T__row6_col2&#x22; class=&#x22;data row6 col2&#x22; >60.00</td><td id=&#x22;T__row6_col3&#x22; class=&#x22;data row6 col3&#x22; >4,920.00</td></tr><tr><th id=&#x22;T__level0_row7&#x22; class=&#x22;row_heading level0 row7&#x22; rowspan=&#x22;5&#x22;>Hoodie</th><th id=&#x22;T__level1_row7&#x22; class=&#x22;row_heading level1 row7&#x22; ></th><td id=&#x22;T__row7_col0&#x22; class=&#x22;data row7 col0&#x22; >738</td><td id=&#x22;T__row7_col1&#x22; class=&#x22;data row7 col1&#x22; >1186.0</td><td id=&#x22;T__row7_col2&#x22; class=&#x22;data row7 col2&#x22; >47.33</td><td id=&#x22;T__row7_col3&#x22; class=&#x22;data row7 col3&#x22; >34,932.00</td></tr><tr><th id=&#x22;T__level1_row8&#x22; class=&#x22;row_heading level1 row8&#x22; >Basic</th><td id=&#x22;T__row8_col0&#x22; class=&#x22;data row8 col0&#x22; >164</td><td id=&#x22;T__row8_col1&#x22; class=&#x22;data row8 col1&#x22; >264.0</td><td id=&#x22;T__row8_col2&#x22; class=&#x22;data row8 col2&#x22; >47.00</td><td id=&#x22;T__row8_col3&#x22; class=&#x22;data row8 col3&#x22; >7,708.00</td></tr><tr><th id=&#x22;T__level1_row9&#x22; class=&#x22;row_heading level1 row9&#x22; >Mega</th><td id=&#x22;T__row9_col0&#x22; class=&#x22;data row9 col0&#x22; >164</td><td id=&#x22;T__row9_col1&#x22; class=&#x22;data row9 col1&#x22; >261.0</td><td id=&#x22;T__row9_col2&#x22; class=&#x22;data row9 col2&#x22; >46.50</td><td id=&#x22;T__row9_col3&#x22; class=&#x22;data row9 col3&#x22; >7,626.00</td></tr><tr><th id=&#x22;T__level1_row10&#x22; class=&#x22;row_heading level1 row10&#x22; >NewBrand</th><td id=&#x22;T__row10_col0&#x22; class=&#x22;data row10 col0&#x22; >164</td><td id=&#x22;T__row10_col1&#x22; class=&#x22;data row10 col1&#x22; >262.0</td><td id=&#x22;T__row10_col2&#x22; class=&#x22;data row10 col2&#x22; >48.50</td><td id=&#x22;T__row10_col3&#x22; class=&#x22;data row10 col3&#x22; >7,954.00</td></tr><tr><th id=&#x22;T__level1_row11&#x22; class=&#x22;row_heading level1 row11&#x22; >Over</th><td id=&#x22;T__row11_col0&#x22; class=&#x22;data row11 col0&#x22; >246</td><td id=&#x22;T__row11_col1&#x22; class=&#x22;data row11 col1&#x22; >399.0</td><td id=&#x22;T__row11_col2&#x22; class=&#x22;data row11 col2&#x22; >47.33</td><td id=&#x22;T__row11_col3&#x22; class=&#x22;data row11 col3&#x22; >11,644.00</td></tr><tr><th id=&#x22;T__level0_row12&#x22; class=&#x22;row_heading level0 row12&#x22; rowspan=&#x22;5&#x22;>Shoes</th><th id=&#x22;T__level1_row12&#x22; class=&#x22;row_heading level1 row12&#x22; ></th><td id=&#x22;T__row12_col0&#x22; class=&#x22;data row12 col0&#x22; >1310</td><td id=&#x22;T__row12_col1&#x22; class=&#x22;data row12 col1&#x22; >2123.0</td><td id=&#x22;T__row12_col2&#x22; class=&#x22;data row12 col2&#x22; >60.00</td><td id=&#x22;T__row12_col3&#x22; class=&#x22;data row12 col3&#x22; >78,600.00</td></tr><tr><th id=&#x22;T__level1_row13&#x22; class=&#x22;row_heading level1 row13&#x22; >Basic</th><td id=&#x22;T__row13_col0&#x22; class=&#x22;data row13 col0&#x22; >409</td><td id=&#x22;T__row13_col1&#x22; class=&#x22;data row13 col1&#x22; >661.0</td><td id=&#x22;T__row13_col2&#x22; class=&#x22;data row13 col2&#x22; >60.00</td><td id=&#x22;T__row13_col3&#x22; class=&#x22;data row13 col3&#x22; >24,540.00</td></tr><tr><th id=&#x22;T__level1_row14&#x22; class=&#x22;row_heading level1 row14&#x22; >Mega</th><td id=&#x22;T__row14_col0&#x22; class=&#x22;data row14 col0&#x22; >328</td><td id=&#x22;T__row14_col1&#x22; class=&#x22;data row14 col1&#x22; >532.0</td><td id=&#x22;T__row14_col2&#x22; class=&#x22;data row14 col2&#x22; >60.00</td><td id=&#x22;T__row14_col3&#x22; class=&#x22;data row14 col3&#x22; >19,680.00</td></tr><tr><th id=&#x22;T__level1_row15&#x22; class=&#x22;row_heading level1 row15&#x22; >NewBrand</th><td id=&#x22;T__row15_col0&#x22; class=&#x22;data row15 col0&#x22; >327</td><td id=&#x22;T__row15_col1&#x22; class=&#x22;data row15 col1&#x22; >531.0</td><td id=&#x22;T__row15_col2&#x22; class=&#x22;data row15 col2&#x22; >60.00</td><td id=&#x22;T__row15_col3&#x22; class=&#x22;data row15 col3&#x22; >19,620.00</td></tr><tr><th id=&#x22;T__level1_row16&#x22; class=&#x22;row_heading level1 row16&#x22; >Over</th><td id=&#x22;T__row16_col0&#x22; class=&#x22;data row16 col0&#x22; >246</td><td id=&#x22;T__row16_col1&#x22; class=&#x22;data row16 col1&#x22; >399.0</td><td id=&#x22;T__row16_col2&#x22; class=&#x22;data row16 col2&#x22; >60.00</td><td id=&#x22;T__row16_col3&#x22; class=&#x22;data row16 col3&#x22; >14,760.00</td></tr><tr><th id=&#x22;T__level0_row17&#x22; class=&#x22;row_heading level0 row17&#x22; rowspan=&#x22;5&#x22;>T-shirt</th><th id=&#x22;T__level1_row17&#x22; class=&#x22;row_heading level1 row17&#x22; ></th><td id=&#x22;T__row17_col0&#x22; class=&#x22;data row17 col0&#x22; >1230</td><td id=&#x22;T__row17_col1&#x22; class=&#x22;data row17 col1&#x22; >1991.0</td><td id=&#x22;T__row17_col2&#x22; class=&#x22;data row17 col2&#x22; >22.00</td><td id=&#x22;T__row17_col3&#x22; class=&#x22;data row17 col3&#x22; >27,060.00</td></tr><tr><th id=&#x22;T__level1_row18&#x22; class=&#x22;row_heading level1 row18&#x22; >Basic</th><td id=&#x22;T__row18_col0&#x22; class=&#x22;data row18 col0&#x22; >328</td><td id=&#x22;T__row18_col1&#x22; class=&#x22;data row18 col1&#x22; >533.0</td><td id=&#x22;T__row18_col2&#x22; class=&#x22;data row18 col2&#x22; >22.00</td><td id=&#x22;T__row18_col3&#x22; class=&#x22;data row18 col3&#x22; >7,216.00</td></tr><tr><th id=&#x22;T__level1_row19&#x22; class=&#x22;row_heading level1 row19&#x22; >Mega</th><td id=&#x22;T__row19_col0&#x22; class=&#x22;data row19 col0&#x22; >328</td><td id=&#x22;T__row19_col1&#x22; class=&#x22;data row19 col1&#x22; >529.0</td><td id=&#x22;T__row19_col2&#x22; class=&#x22;data row19 col2&#x22; >21.50</td><td id=&#x22;T__row19_col3&#x22; class=&#x22;data row19 col3&#x22; >7,052.00</td></tr><tr><th id=&#x22;T__level1_row20&#x22; class=&#x22;row_heading level1 row20&#x22; >NewBrand</th><td id=&#x22;T__row20_col0&#x22; class=&#x22;data row20 col0&#x22; >328</td><td id=&#x22;T__row20_col1&#x22; class=&#x22;data row20 col1&#x22; >528.0</td><td id=&#x22;T__row20_col2&#x22; class=&#x22;data row20 col2&#x22; >22.50</td><td id=&#x22;T__row20_col3&#x22; class=&#x22;data row20 col3&#x22; >7,380.00</td></tr><tr><th id=&#x22;T__level1_row21&#x22; class=&#x22;row_heading level1 row21&#x22; >Over</th><td id=&#x22;T__row21_col0&#x22; class=&#x22;data row21 col0&#x22; >246</td><td id=&#x22;T__row21_col1&#x22; class=&#x22;data row21 col1&#x22; >401.0</td><td id=&#x22;T__row21_col2&#x22; class=&#x22;data row21 col2&#x22; >22.00</td><td id=&#x22;T__row21_col3&#x22; class=&#x22;data row21 col3&#x22; >5,412.00</td></tr><tr><th id=&#x22;T__level0_row22&#x22; class=&#x22;row_heading level0 row22&#x22; rowspan=&#x22;3&#x22;>Table</th><th id=&#x22;T__level1_row22&#x22; class=&#x22;row_heading level1 row22&#x22; ></th><td id=&#x22;T__row22_col0&#x22; class=&#x22;data row22 col0&#x22; >492</td><td id=&#x22;T__row22_col1&#x22; class=&#x22;data row22 col1&#x22; >788.0</td><td id=&#x22;T__row22_col2&#x22; class=&#x22;data row22 col2&#x22; >255.00</td><td id=&#x22;T__row22_col3&#x22; class=&#x22;data row22 col3&#x22; >125,460.00</td></tr><tr><th id=&#x22;T__level1_row23&#x22; class=&#x22;row_heading level1 row23&#x22; >Basic</th><td id=&#x22;T__row23_col0&#x22; class=&#x22;data row23 col0&#x22; >246</td><td id=&#x22;T__row23_col1&#x22; class=&#x22;data row23 col1&#x22; >392.0</td><td id=&#x22;T__row23_col2&#x22; class=&#x22;data row23 col2&#x22; >240.00</td><td id=&#x22;T__row23_col3&#x22; class=&#x22;data row23 col3&#x22; >59,040.00</td></tr><tr><th id=&#x22;T__level1_row24&#x22; class=&#x22;row_heading level1 row24&#x22; >Mega</th><td id=&#x22;T__row24_col0&#x22; class=&#x22;data row24 col0&#x22; >246</td><td id=&#x22;T__row24_col1&#x22; class=&#x22;data row24 col1&#x22; >396.0</td><td id=&#x22;T__row24_col2&#x22; class=&#x22;data row24 col2&#x22; >270.00</td><td id=&#x22;T__row24_col3&#x22; class=&#x22;data row24 col3&#x22; >66,420.00</td></tr></tbody></table>" width="100%" height="500" frameborder="0" />

#### Adding style with CSS

Before being a PDF, our report will be an HTML file so we can use CSS to style it:

```pycon theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
>>> table_css 
```

```css theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
table {
  border-collapse: collapse;
}

td {
  border: 1px solid black;
  padding: 8px;
  text-align: right;
}

th {
  border: 1px solid black;
  padding: 8px;
  text-align: center;
}

thead {
  background-color: lightgray;
}
```

### Creating the chart

Many Python libraries can create charts from pandas DataFrames.
In this how-to, we’ll use [seaborn](https://seaborn.pydata.org) which is a wrapper around [Matplotlib](https://matplotlib.org):

```pycon theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
>>> import io
>>> import matplotlib_inline.backend_inline
>>> # Render charts as vector graphics so they stay sharp at any resolution.
>>> matplotlib_inline.backend_inline.set_matplotlib_formats("svg")
>>> import matplotlib.pyplot as plt
>>> import seaborn as sns
```

```pycon theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
>>> data = cube.query(
...     m["Quantity.SUM"], levels=[l["Sub category"]]
... ).reset_index()
>>> _ = sns.barplot(
...     data=data,
...     x="Sub category",
...     y="Quantity.SUM",
... ).set(title="Quantity.SUM by Sub category")
>>> chart = io.BytesIO()
>>> plt.savefig(chart, format="svg", metadata={"Date": None})
```

<img src="https://mintcdn.com/activeviam/Eh3Su73oAmRCnbdN/engine/python-sdk/6.2.0b0/guides/__resources__/generating_pdf_reports/quantity_by_sub_category.svg?fit=max&auto=format&n=Eh3Su73oAmRCnbdN&q=85&s=21827b4f1a7de3ddaafea5d1cc0f6b9c" alt="Quantity.SUM by Sub category" width="614" height="461" data-path="engine/python-sdk/6.2.0b0/guides/__resources__/generating_pdf_reports/quantity_by_sub_category.svg" />

## Creating the HTML report

We’ll use [Jinja](https://jinja.palletsprojects.com) to render this template:

```pycon theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
>>> template_path = resources_directory / "template.jinja"
>>> template_code = template_path.read_text()
>>> template_code  
```

```html theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
<html>
  <head>
    <style>
      {{ table_css }}
    </style>
  </head>
  <body>
    <header
      style="
        display: flex;
        align-items: center;
        justify-content: space-between;
        width: 100%;
      "
    >
      <h1>{{ report_name }}</h1>
      <h5>Report Date: {{ date }}</h5>
    </header>
    <div>
      <h3>{{ table_title }}</h3>
      <div>{{ table }}</div>
    </div>
    <br />
    <div>{{ chart }}</div>
  </body>
</html>
```

With this data:

```pycon theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
>>> from datetime import date
>>> template_data = {
...     "chart": chart.getvalue().decode(),
...     "date": date(2024, 8, 10),
...     "report_name": "Atoti PDF Report",
...     "table": df_style.to_html(),
...     "table_css": table_css,
...     "table_title": "Measures by Sub category and Brand",
... }
```

```pycon theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
>>> from jinja2 import Environment, FileSystemLoader
>>> environment = Environment(
...     autoescape=False,
...     loader=FileSystemLoader(template_path.parent),
... )
>>> template = environment.get_template(template_path.name)
>>> html = template.render(template_data)
```

## Exporting the report as a PDF

[WeasyPrint](https://doc.courtbouillon.org/weasyprint/stable) can render HTML as a PDF:

```pycon theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
>>> import weasyprint
>>> pdf_path = output_directory / "report.pdf"
>>> document = weasyprint.HTML(string=html)
>>> document.write_pdf(pdf_path)
```

<iframe src="../_static/report.pdf" width="800" height="800" />

## Going further

The report could be [sent by email](https://realpython.com/python-send-email/#adding-attachments-using-the-email-package) periodically using the [schedule](https://schedule.readthedocs.io) library or:

* Task Scheduler on Windows
* `cron` on Linux or macOS
* A Lambda function in the cloud
