Code Used in documentation

Basic Usage

# start_imports
import os
import tempfile
import pandas as pd
from table_compositor.table_compositor import build_presentation_model

# There are equivalent classes for using xlsxwriter library. Namely,
# XlsxWriterCompositor and XlsxWriterStyleHelper
from table_compositor.xlsx_writer import OpenPyxlCompositor
from table_compositor.xlsx_styles import OpenPyxlStyleHelper

# end_imports

# start_basic_example_2
def basic_example2():

    df = pd.DataFrame(dict(a=[10, 20, 30, 40, 50], b=[0.1, 0.9,0.2, 0.6,0.3]), index=[1,2,3,4,5])

    def style_func(idx, col):
        if col == 'b':
            return OpenPyxlStyleHelper.get_style(number_format='0.00%')
        else:
            # for 'a' we do dollar format
            return OpenPyxlStyleHelper.get_style(number_format='$#,##.00')

    # create a presentation model
    # note the OpenPyxlStyleHelper function available in xlsx_styles module. But a return value of style function
    # can be any dict whose keys are attributes of the OpenPyxl cell object.
    presentation_model = build_presentation_model(
        df=df,
        data_value_func=lambda idx, col: df.loc[idx, col] * 10 if col == 'a' else df.loc[idx, col],
        data_style_func=style_func,
        header_value_func=lambda node: node.value.capitalize(),
        header_style_func=lambda _: OpenPyxlStyleHelper.default_header_style(),
        index_value_func=lambda node: node.value * 100,
        index_style_func=lambda _: OpenPyxlStyleHelper.default_header_style(),
        index_name_func=lambda _: 'Basic Example',
        index_name_style_func=lambda _: OpenPyxlStyleHelper.default_header_style())

    # create a layout, which is usually a nested list of presentation models
    layout = [presentation_model]

    # render to xlsx
    output_fp = os.path.join(tempfile.gettempdir(), 'basic_example2.xlsx')
    OpenPyxlCompositor.to_xlsx(layout=layout, output_fp=output_fp)

# end_basic_example_2

# start_basic_example_3
def basic_example3():

    df = pd.DataFrame(dict(a=[10, 20, 30, 40],
                           b=[0.1, 0.9,0.2, 0.6],
                           d=[50, 60, 70, 80],
                           e=[200, 300, 400, 500]))
    df.columns = pd.MultiIndex.from_tuples([('A', 'x'), ('A', 'y'), ('B', 'x'), ('B', 'y')])
    df.index = pd.MultiIndex.from_tuples([(1, 100), (1, 200), (2, 100), (2, 200)])
    print(df)

    def index_style_func(node):
        # node.key here could be one of (1,), (1, 100), (2,), (2, 100), (2, 200)
        bg_color = 'FFFFFF'
        if node.key == (1,) or node.key == (2,):
            bg_color = '9E80B8'
        elif node.key[1] == 100:
            bg_color = '4F90C1'
        elif node.key[1] == 200:
            bg_color = '6DC066'
        return OpenPyxlStyleHelper.get_style(bg_color=bg_color)

    def header_style_func(node):
        bg_color = 'FFFFFF'
        if node.key == ('A',) or node.key == ('B',):
            bg_color = '9E80B8'
        elif node.key[1] == 'x':
            bg_color = '4F90C1'
        elif node.key[1] == 'y':
            bg_color = '6DC066'
        return OpenPyxlStyleHelper.get_style(bg_color=bg_color)

    # create a presentation model
    # note the OpenPyxlStyleHeloer function available in xlsx_styles module. But a return value of style function
    # can be any dict whose keys are attributes of the OpenPyxl cell object.
    presentation_model = build_presentation_model(
        df=df,
        index_style_func=index_style_func,
        header_style_func=header_style_func,
        index_name_func=lambda _: 'Multi-Hierarchy Example')

    # create a layout, which is usually a nested list of presentation models
    layout = [presentation_model]

    # render to xlsx
    output_fp = os.path.join(tempfile.gettempdir(), 'basic_example3.xlsx')
    OpenPyxlCompositor.to_xlsx(layout=layout, output_fp=output_fp)

# end_basic_example_3


# start_layout_example_1
def layout_example1():

    df = pd.DataFrame(dict(a=[10, 20, 30, 40, 50], b=[0.1, 0.9,0.2, 0.6,0.3]), index=[1,2,3,4,5])

    def style_func(idx, col):
        if col == 'b':
            return OpenPyxlStyleHelper.get_style(number_format='0.00%')
        else:
            # for 'a' we do dollar format
            return OpenPyxlStyleHelper.get_style(number_format='$#,##.00')

    # create a presentation model
    # note the OpenPyxlStyleHeloer function available in xlsx_styles module. But a return value of style function
    # can be any dict whose keys are attributes of the OpenPyxl cell object.
    presentation_model = build_presentation_model(
        df=df,
        data_value_func=lambda idx, col: df.loc[idx, col] * 10 if col == 'a' else df.loc[idx, col],
        data_style_func=style_func,
        header_value_func=lambda node: node.value.capitalize(),
        header_style_func=lambda _: OpenPyxlStyleHelper.default_header_style(),
        index_value_func=lambda node: node.value * 100,
        index_style_func=lambda _: OpenPyxlStyleHelper.default_header_style(),
        index_name_func=lambda _: 'Basic Example',
        index_name_style_func=lambda _: OpenPyxlStyleHelper.default_header_style())

    # start_layout_code_1
    # create a layout, which is usually a nested list of presentation models
    layout = [[presentation_model], [[presentation_model], [presentation_model]]]

    # render to xlsx
    output_fp = os.path.join(tempfile.gettempdir(), 'layout_vertical_example1.xlsx')
    # the default value for orientation is 'vertical'
    OpenPyxlCompositor.to_xlsx(layout=layout, output_fp=output_fp, orientation='vertical')

    output_fp = os.path.join(tempfile.gettempdir(), 'layout_horizontal_example1.xlsx')
    OpenPyxlCompositor.to_xlsx(layout=layout, output_fp=output_fp, orientation='horizontal')
    print('Writing xlsx file=', output_fp)

    # mutiple nesting
    layout_complex = [presentation_model,
                      [presentation_model, [presentation_model, presentation_model]]]

    output_fp = os.path.join(tempfile.gettempdir(), 'layout_complex_example1.xlsx')
    OpenPyxlCompositor.to_xlsx(layout=layout_complex, output_fp=output_fp, orientation='vertical')
    print('Writing xlsx file=', output_fp)
    # end_layout_code_1

# end_layout_example_1


if __name__ == '__main__'    :
    basic_example2()
    basic_example3()
    layout_example1()

XLSX Examples

'''
This module is referred to by the Sphinx documentation. If you need to run this
module, install table_compositor in an separate  environment and then run this module
in that environment. This helps the imports find the modules in the right place
'''

# start_imports
import tempfile
import zipfile
import collections
import os
import webbrowser

import requests
import pandas as pd

import table_compositor.table_compositor as tc
import table_compositor.xlsx_writer as xlsxw
import table_compositor.xlsx_styles as xlsstyle
# end_imports

# start_data_routine
# code snippet adapted from http://function-pipe.readthedocs.io/en/latest/usage_df.html
# source url
URL_NAMES = 'https://www.ssa.gov/oact/babynames/names.zip'
ZIP_NAME = 'names.zip'

def load_names_data():
    fp = os.path.join(tempfile.gettempdir(), ZIP_NAME)
    if not os.path.exists(fp):
        r = requests.get(URL_NAMES)
        with open(fp, 'wb') as f:
            f.write(r.content)

    post = collections.OrderedDict()
    with zipfile.ZipFile(fp) as zf:
        # get ZipInfo instances
        for zi in sorted(zf.infolist(), key=lambda zi: zi.filename):
            fn = zi.filename
            if fn.startswith('yob'):
                year = int(fn[3:7])
                df = pd.read_csv(
                    zf.open(zi),
                    header=None,
                    names=('name', 'gender', 'count'))
                df['year'] = year
                post[year] = df

        df = pd.concat(post.values())
        df.set_index('name', inplace=True, drop=True)
        return df

def sample_names_data():
    df = load_names_data()
    df = df[(df['year'] == 2015) & (df['count'] > 1000)]
    return df.sample(100, random_state=0).sort_values('count')

def top_names_for_year(year=2015, gender='F', top_n=5):
    df = load_names_data()
    df = df[(df['year'] == year) & (df['gender'] == gender)]
    df = df.sort_values('count')[:top_n]
    return df
# end_data_routine

# start_XLSXExample1
class XLSXExample1:
    '''
    Demonstrates rendering a simple dataframe to a xlsx file
    using the default styles
    '''
    @classmethod
    def render_xlsx(cls):
        '''
        Render the df to a xlsx file.
        '''

        # load data
        df = sample_names_data()
        # build presentation model
        pm = tc.build_presentation_model(df=df, output_format='xlsx')

        # render to xlsx
        tempdir = tempfile.gettempdir()
        fp = os.path.join(tempdir, 'example1.xlsx')
        layout = [pm]
        print('Writing to ' + fp)
        xlsxw.OpenPyxlCompositor.to_xlsx(layout=layout, output_fp=fp)
# end_XLSXExample1


# start_XLSXExample2
class XLSXExample2:
    '''
    Demonstrates using call-backs that help set the display and style
    properties of each cell in the xlsx sheet.
    '''

    @staticmethod
    def data_value_func(df):
        def _inner(idx, col):
            if col == 'gender':
                if df.loc[idx, col] == 'F':
                    return "Female"
                return 'Male'
            return df.loc[idx, col]
        return _inner

    @staticmethod
    def data_style_func(df):
        def _inner(idx, col):
            bg_color = None
            number_format='General'
            if col == 'count':
                number_format='#,##0'
            if df.loc[idx, 'gender'] == 'F':
                bg_color = 'bbdef8'
            else:
                bg_color = 'e3f2fd'
            return xlsstyle.OpenPyxlStyleHelper.get_style(
                bg_color=bg_color,
                number_format=number_format)
        return _inner

    @staticmethod
    def index_name_value_func(value):
        return value.capitalize()

    @staticmethod
    def index_name_style_func(value):
        return xlsstyle.OpenPyxlStyleHelper.default_header_style()

    @staticmethod
    def header_value_func(node):
        return node.value.capitalize()

    @staticmethod
    def header_style_func(node):
        return xlsstyle.OpenPyxlStyleHelper.default_header_style()

    @staticmethod
    def index_value_func(node):
        return node.value.capitalize()

    @staticmethod
    def index_style_func(df):
        def _inner(node):
            bg_color = None
            if df.loc[node.value, 'gender'] == 'F':
                bg_color = 'bbdef8'
            else:
                bg_color = 'e3f2fd'
            return xlsstyle.OpenPyxlStyleHelper.get_style(bg_color=bg_color)
        return _inner

    @classmethod
    def render_xlsx(cls):
        # load data
        df = sample_names_data()
        # build presentation model
        klass_ = XLSXExample2
        pm = tc.build_presentation_model(
            df=df,
            output_format='xlsx',
            data_value_func=klass_.data_value_func(df),
            data_style_func=klass_.data_style_func(df),
            header_value_func=klass_.header_value_func,
            header_style_func=klass_.header_style_func,
            index_style_func=klass_.index_style_func(df),
            index_value_func=klass_.index_value_func,
            index_name_style_func=klass_.index_name_style_func,
            index_name_func=klass_.index_name_value_func)

        # render to xlsx
        tempdir = tempfile.gettempdir()
        fp = os.path.join(tempdir, 'example2.xlsx')
        layout = [pm]
        print('Writing to ' + fp)
        xlsxw.OpenPyxlCompositor.to_xlsx(layout=layout, output_fp=fp)
# end_XLSXExample2

# start_XLSXExample3
class XLSXExample3:
    '''
    Demonstrates using call-backs and also rendering multiple tables to single
    worksheet.
    '''

    @staticmethod
    def data_value_func(df):
        def _inner(idx, col):
            if col == 'gender':
                if df.loc[idx, col] == 'F':
                    return "Female"
                return 'Male'
            return df.loc[idx, col]
        return _inner

    @staticmethod
    def data_style_func(df):
        def _inner(idx, col):
            bg_color = None
            number_format='General'
            if col == 'count':
                number_format='#,##0'
            if df.loc[idx, 'gender'] == 'F':
                bg_color = 'bbdef8'
            else:
                bg_color = 'e3f2fd'
            return xlsstyle.OpenPyxlStyleHelper.get_style(
                bg_color=bg_color,
                number_format=number_format)
        return _inner

    @staticmethod
    def index_name_value_func(value):
        return value.capitalize()

    @staticmethod
    def index_name_style_func(value):
        return xlsstyle.OpenPyxlStyleHelper.default_header_style()

    @staticmethod
    def header_value_func(node):
        return node.value.capitalize()

    @staticmethod
    def header_style_func(node):
        return xlsstyle.OpenPyxlStyleHelper.default_header_style()

    @staticmethod
    def index_value_func(node):
        return node.value.capitalize()

    @staticmethod
    def index_style_func(df):
        def _inner(node):
            bg_color = None
            if df.loc[node.value, 'gender'] == 'F':
                bg_color = 'bbdef8'
            else:
                bg_color = 'e3f2fd'
            return xlsstyle.OpenPyxlStyleHelper.get_style(bg_color=bg_color)
        return _inner


    @classmethod
    def render_xlsx(cls):
        # Prepare first data frame (same as in render_xlsx)
        df = sample_names_data()
        # build presentation model
        klass_ = XLSXExample3
        pm_all = tc.build_presentation_model(
            df=df,
            output_format='xlsx',
            data_value_func=klass_.data_value_func(df),
            data_style_func=klass_.data_style_func(df),
            header_value_func=klass_.header_value_func,
            header_style_func=klass_.header_style_func,
            index_style_func=klass_.index_style_func(df),
            index_value_func=klass_.index_value_func,
            index_name_style_func=klass_.index_name_style_func,
            index_name_func=klass_.index_name_value_func)

        male_df = top_names_for_year(gender='M')
        pm_top_male = tc.build_presentation_model(
            df=male_df,
            output_format='xlsx',
            data_value_func=klass_.data_value_func(male_df),
            data_style_func=klass_.data_style_func(male_df),
            header_value_func=klass_.header_value_func,
            header_style_func=klass_.header_style_func,
            index_style_func=klass_.index_style_func(male_df),
            index_value_func=klass_.index_value_func,
            index_name_style_func=klass_.index_name_style_func,
            index_name_func=klass_.index_name_value_func)

        female_df = top_names_for_year(gender='F')
        pm_top_female = tc.build_presentation_model(
            df=female_df,
            output_format='xlsx',
            data_value_func=klass_.data_value_func(female_df),
            data_style_func=klass_.data_style_func(female_df),
            header_value_func=klass_.header_value_func,
            header_style_func=klass_.header_style_func,
            index_style_func=klass_.index_style_func(female_df),
            index_value_func=klass_.index_value_func,
            index_name_style_func=klass_.index_name_style_func,
            index_name_func=klass_.index_name_value_func)


        layout = [pm_all, [pm_top_female, pm_top_male]]
        # render to xlsx
        tempdir = tempfile.gettempdir()
        fp = os.path.join(tempdir, 'example3.xlsx')
        print('Writing to ' + fp)
        xlsxw.OpenPyxlCompositor.to_xlsx(layout=layout, output_fp=fp, orientation='horizontal')
# end_XLSXExample3

# start_XLSXExample4
class XLSXExample4:
    '''
    Demonstrate styling and rendering of multi-hierarchical indexed dataframe
    into a xlsx file.
    '''

    @staticmethod
    def data_style_func(df):
        def _inner(idx, col):
            bg_color = None
            number_format = 'General'
            if col == 'count':
                number_format = '#,##0'
            if idx[1] == 'F':
                bg_color = 'bbdef8'
            else:
                bg_color = 'e3f2fd'
            return xlsstyle.OpenPyxlStyleHelper.get_style(
                bg_color=bg_color,
                number_format=number_format)
        return _inner

    @staticmethod
    def index_name_value_func(value):
        return 'Max By Year'

    @staticmethod
    def index_name_style_func(value):
        return xlsstyle.OpenPyxlStyleHelper.default_header_style()

    @staticmethod
    def header_value_func(node):
        return node.value.capitalize()

    @staticmethod
    def header_style_func(node):
        return xlsstyle.OpenPyxlStyleHelper.default_header_style()

    @staticmethod
    def index_value_func(node):
        if isinstance(node.value, str):
            return node.value.capitalize()
        return node.value

    @staticmethod
    def index_style_func(df):
        def _inner(node):
            bg_color = None
            if len(node.key) == 1:
                bg_color = '4f81bd'
            elif node.key[1] == 'F':
                bg_color = 'bbdef8'
            else:
                bg_color = 'e3f2fd'
            return xlsstyle.OpenPyxlStyleHelper.get_style(bg_color=bg_color)
        return _inner

    @classmethod
    def render_xlsx(cls):

        # Prepare first data frame (same as in render_xlsx)
        data_df = load_names_data()
        data_df = data_df[data_df['year'] >= 2000]
        g = data_df.groupby(('year', 'gender'))
        df = g.max()

        klass_ = cls
        pm = tc.build_presentation_model(
            df=df,
            output_format='xlsx',
            #data_value_func=None,   # use default
            data_style_func=klass_.data_style_func(df),
            header_value_func=klass_.header_value_func,
            header_style_func=klass_.header_style_func,
            index_style_func=klass_.index_style_func(df),
            index_value_func=klass_.index_value_func,
            index_name_style_func=klass_.index_name_style_func,
            index_name_func=klass_.index_name_value_func)

        layout = [pm]
        # render to xlsx
        tempdir = tempfile.gettempdir()
        fp = os.path.join(tempdir, 'example4.xlsx')
        print('Writing to ' + fp)
        xlsxw.OpenPyxlCompositor.to_xlsx(layout=layout, output_fp=fp, orientation='horizontal')
# end_XLSXExample4

def main():
    XLSXExample1.render_xlsx()
    XLSXExample2.render_xlsx()
    XLSXExample3.render_xlsx()
    XLSXExample4.render_xlsx()

if __name__ == '__main__':
    main()

HTML Examples

'''
This module is referred to by the Sphinx documentation. If you need to run this
module, install table_compositor in an separate  environment and then run this module
in that environment. This helps the imports find the modules in the right place
'''


# start_imports
import tempfile
import zipfile
import collections
import os
import webbrowser

import requests
import pandas as pd

import table_compositor.table_compositor as tc
import table_compositor.html_writer as htmlw
import table_compositor.html_styles as html_style

# end_imports

# start_data_routine
# code snippet adapted from http://function-pipe.readthedocs.io/en/latest/usage_df.html
# source url
URL_NAMES = 'https://www.ssa.gov/oact/babynames/names.zip'
ZIP_NAME = 'names.zip'

def load_names_data():
    fp = os.path.join(tempfile.gettempdir(), ZIP_NAME)
    if not os.path.exists(fp):
        r = requests.get(URL_NAMES)
        with open(fp, 'wb') as f:
            f.write(r.content)

    post = collections.OrderedDict()
    with zipfile.ZipFile(fp) as zf:
        # get ZipInfo instances
        for zi in sorted(zf.infolist(), key=lambda zi: zi.filename):
            fn = zi.filename
            if fn.startswith('yob'):
                year = int(fn[3:7])
                df = pd.read_csv(
                    zf.open(zi),
                    header=None,
                    names=('name', 'gender', 'count'))
                df['year'] = year
                post[year] = df

        df = pd.concat(post.values())
        df.set_index('name', inplace=True, drop=True)
        return df

def sample_names_data():
    df = load_names_data()
    df = df[(df['year'] == 2015) & (df['count'] > 1000)]
    return df.sample(50, random_state=0).sort_values('count')

def top_names_for_year(year=2015, gender='F', top_n=5):
    df = load_names_data()
    df = df[(df['year'] == year) & (df['gender'] == gender)]
    df = df.sort_values('count')[:top_n]
    return df
# end_data_routine

# start_HTMLExample1
class HTMLExample1:
    '''
    Demonstrate rendering of a simple dataframe into html
    '''
    @classmethod
    def render_html(cls):

        # load data
        df = load_names_data()
        df = df[:100]

        # build presentation model
        pm = tc.build_presentation_model(df=df, output_format='html')

        # render to xlsx
        tempdir = tempfile.gettempdir()
        fp = os.path.join(tempdir, 'example_1.html')
        layout = [pm]
        print('Writing to ' + fp)
        html = htmlw.HTMLWriter.to_html(layout, border=1)
        output_fp = os.path.join(
            tempfile.gettempdir(),
            'example1.html')
        with open(output_fp, 'w') as f:
            f.write(html)
# end_HTMLExample1

# start_HTMLExample2
class HTMLExample2:
    '''
    Demonstrate rendering of a simple dataframe into html
    '''

    @staticmethod
    def data_value_func(df):
        def _inner(idx, col):
            if col == 'gender':
                if df.loc[idx, col] == 'F':
                    return "Female"
                return 'Male'
            return df.loc[idx, col]
        return _inner

    @staticmethod
    def data_style_func(df):
        def _inner(idx, col):
            color = '#FFFFFF'
            text_align = 'left'
            if col == 'count':
                text_align = 'right'
            if df.loc[idx, 'gender'] == 'F':
                color = '#bbdef8'
            else:
                color = '#e3f2fd'
            return html_style.td_style(
                text_align=text_align,
                background_color=color,
                color='#000000',
                font_weight='normal',
                white_space='pre',
                padding='10px',
                border=None)
        return _inner

    @staticmethod
    def index_name_value_func(value):
        return value.capitalize()

    @staticmethod
    def header_value_func(node):
        return node.value.capitalize()

    @staticmethod
    def header_style_func(node):
        return html_style.td_style(
            text_align='center',
            background_color='#4F81BD',
            color='#FFFFFF',
            font_weight='bold',
            white_space='pre',
            padding='10px',
            border=1)

    @staticmethod
    def index_value_func(node):
        return node.value.capitalize()

    @staticmethod
    def index_style_func(node):
        return html_style.td_style(
            text_align='center',
            background_color='#4F81BD',
            color='#FFFFFF',
            font_weight='bold',
            white_space='pre',
            padding='10px',
            border=1)

    @classmethod
    def render_html(cls):
        # load data
        df = sample_names_data()
        # build presentation model
        klass_ = HTMLExample2
        pm = tc.build_presentation_model(
            df=df,
            output_format='html',
            data_value_func=klass_.data_value_func(df),
            data_style_func=klass_.data_style_func(df),
            header_value_func=klass_.header_value_func,
            header_style_func=klass_.header_style_func,
            index_style_func=klass_.index_style_func,
            index_value_func=klass_.index_value_func,
            index_name_func=klass_.index_name_value_func)

        layout = [pm]
        html = htmlw.HTMLWriter.to_html(layout, border=1)
        output_fp = os.path.join(
            tempfile.gettempdir(),
            'example2.html')
        print('Writing to =', output_fp)
        with open(output_fp, 'w') as f:
            f.write(html)
# end_HTMLExample2


# start_HTMLExample3
class HTMLExample3:
    '''
    Demonstrate styling and rendering of multiple multi-hierarchical indexed dataframe
    into a html file
    '''

    @staticmethod
    def data_value_func(df):
        def _inner(idx, col):
            if col == 'gender':
                if df.loc[idx, col] == 'F':
                    return "Female"
                return 'Male'
            return df.loc[idx, col]
        return _inner

    @staticmethod
    def data_style_func(df):
        def _inner(idx, col):
            color = '#FFFFFF'
            text_align = 'left'
            if col == 'count':
                text_align = 'right'
            if df.loc[idx, 'gender'] == 'F':
                color = '#bbdef8'
            else:
                color = '#e3f2fd'
            return html_style.td_style(
                text_align=text_align,
                background_color=color,
                color='#000000',
                font_weight='normal',
                white_space='pre',
                padding='10px',
                border=None)
        return _inner


    @staticmethod
    def index_name_value_func(value):
        return 'Max By Year'

    @staticmethod
    def header_value_func(node):
        return node.value.capitalize()

    @staticmethod
    def header_style_func(node):
        return html_style.td_style(
            text_align='center',
            background_color='#4F81BD',
            color='#FFFFFF',
            font_weight='bold',
            white_space='pre',
            padding='10px',
            border=1)

    @staticmethod
    def index_value_func(node):
        if isinstance(node.value, str):
            return node.value.capitalize()
        return node.value

    @staticmethod
    def index_style_func(node):
        return html_style.td_style(
            text_align='center',
            background_color='#4F81BD',
            color='#FFFFFF',
            font_weight='bold',
            white_space='pre',
            padding='10px',
            border=1)

    @classmethod
    def render_html(cls):

        # Prepare first data frame (same as in render_xlsx)
        df = sample_names_data()
        # build presentation model
        klass_ = HTMLExample4
        pm_all = tc.build_presentation_model(
            df=df,
            output_format='html',
            data_value_func=klass_.data_value_func(df),
            data_style_func=klass_.data_style_func(df),
            header_value_func=klass_.header_value_func,
            header_style_func=klass_.header_style_func,
            index_style_func=klass_.index_style_func,
            index_value_func=klass_.index_value_func,
            index_name_func=lambda _: 'Sample Data')

        male_df = top_names_for_year(gender='M')
        pm_top_male = tc.build_presentation_model(
            df=male_df,
            output_format='html',
            data_value_func=klass_.data_value_func(male_df),
            data_style_func=klass_.data_style_func(male_df),
            header_value_func=klass_.header_value_func,
            header_style_func=klass_.header_style_func,
            index_style_func=klass_.index_style_func,
            index_value_func=klass_.index_value_func,
            index_name_func=lambda _: 'Max by Year')

        female_df = top_names_for_year(gender='F')
        pm_top_female = tc.build_presentation_model(
            df=female_df,
            output_format='html',
            data_value_func=klass_.data_value_func(female_df),
            data_style_func=klass_.data_style_func(female_df),
            header_value_func=klass_.header_value_func,
            header_style_func=klass_.header_style_func,
            index_style_func=klass_.index_style_func,
            index_value_func=klass_.index_value_func,
            index_name_func=lambda _ : 'Max by Year')

        layout = [pm_all, [pm_top_female, pm_top_male]]
        # render to xlsx
        html = htmlw.HTMLWriter.to_html(
            layout, border=1, orientation='horizontal')
        output_fp = os.path.join(
            tempfile.gettempdir(),
            'example3.html')
        print('Writing to =', output_fp)
        with open(output_fp, 'w') as f:
            f.write(html)
# end_HTMLExample3

# start_HTMLExample4
class HTMLExample4:
    '''
    Demonstrate styling and rendering of multi-hierarchical indexed dataframe
    into a html file.
    '''

    @staticmethod
    def data_value_func(df):
        def _inner(idx, col):
            if col == 'gender':
                if df.loc[idx, col] == 'F':
                    return "Female"
                return 'Male'
            return df.loc[idx, col]
        return _inner

    @staticmethod
    def data_style_func(df):
        def _inner(idx, col):
            color = '#FFFFFF'
            text_align = 'left'
            if col == 'count':
                text_align = 'right'
            if idx[1] == 'F':
                color = '#bbdef8'
            else:
                color = '#e3f2fd'

            return html_style.td_style(
                text_align=text_align,
                background_color=color,
                color='#000000',
                font_weight='normal',
                white_space='pre',
                padding='10px',
                border=None)
        return _inner


    @staticmethod
    def index_name_value_func(value):
        return 'Max By Year'

    @staticmethod
    def header_value_func(node):
        return node.value.capitalize()

    @staticmethod
    def header_style_func(node):
        return html_style.td_style(
            text_align='center',
            background_color='#4F81BD',
            color='#FFFFFF',
            font_weight='bold',
            white_space='pre',
            padding='10px',
            border=1)

    @staticmethod
    def index_value_func(node):
        if isinstance(node.value, str):
            return node.value.capitalize()
        return node.value

    @staticmethod
    def index_style_func(node):
        return html_style.td_style(
            text_align='center',
            background_color='#4F81BD',
            color='#FFFFFF',
            font_weight='bold',
            white_space='pre',
            padding='10px',
            border=1)

    @classmethod
    def render_html(cls):

        # Prepare first data frame (same as in render_xlsx)
        data_df = load_names_data()
        data_df = data_df[data_df['year'] >= 2000]
        g = data_df.groupby(('year', 'gender'))
        df = g.max()

        klass_ = cls
        pm = tc.build_presentation_model(
            df=df,
            output_format='html',
            data_value_func=klass_.data_value_func(df),
            data_style_func=klass_.data_style_func(df),
            header_value_func=klass_.header_value_func,
            header_style_func=klass_.header_style_func,
            index_style_func=klass_.index_style_func,
            index_value_func=klass_.index_value_func,
            index_name_func=klass_.index_name_value_func)

        layout = [pm]
        # render to xlsx
        html = htmlw.HTMLWriter.to_html(layout, border=1)
        output_fp = os.path.join(
            tempfile.gettempdir(),
            'example4.html')
        print('Writing to =', output_fp)
        with open(output_fp, 'w') as f:
            f.write(html)
# end_HTMLExample4


def main():
    HTMLExample1.render_html()
    HTMLExample2.render_html()
    HTMLExample3.render_html()
    HTMLExample4.render_html()


if __name__ == '__main__':
    main()