Full Example Report¶
Below you can find a full example report with plenty of comments detailing the inner workings and design philosophy of
basic-report.
Code¶
from basic_report import Report
import datetime
import tempfile
import numpy as np
import pandas as pd
import matplotlib.pylab as plt
from lorem_text import lorem
from pathlib import Path
#-----------------------------------------------------------------------------------------------------------------------
# FAKE DATA PREPARATION
#-----------------------------------------------------------------------------------------------------------------------
errors = ['As most of the time you can use normal <strong>HTML</strong>',
'code to manipulate your error strings',
'You could probably even add tables and such',
'But this is not yet supported on an API level']
warnings = ['This is just one single warning']
info = ['This is just one info']
# Table data
# 100 rows - 5 cols of random variables
columns = ['COL 1', 'COL 2', 'COL 3', 'COL 4', 'COL 5']
table = np.random.rand(100, 5)
table = pd.DataFrame(table, columns=columns)
table.index.name = 'Index Name'
#-----------------------------------------------------------------------------------------------------------------------
# CREATING THE REPORT
#-----------------------------------------------------------------------------------------------------------------------
def make_report():
# Calling Report() prepares everything for the main page index.html
r = Report(
report_dir='output_dir',
report_title='Example Report',
report_date=datetime.date.today(),
color_mode='light',
config_file=None,
text_color='slate',
)
# First add the big report header. You don't have to do this if you just want to add plots, etc.
r.add_report_header(color='forest')
# Now we want to add the error/info/warning section which provides information about all the issues encountered
r.add_header('Errors / warnings / info section')
r.add_text('This is what the error/warning/info section looks like if there are no things to report on')
r.add_error_warning_info_section(errors=[], warnings=[], info=[])
r.add_text('And this is the section when there are things to report on')
r.add_error_warning_info_section(errors=errors, warnings=warnings, info=info)
# You can adjust the color of many elements, e.g., headers and subheaders. You can also use standard HTML syntax
# in your text, e.g., to provide a link to an external page. You can also choose your text alignment when adding
# a text element
r.add_header('Columns <a href=www.google.com>www.google.com</a>', color='cherry')
r.add_text(lorem.paragraph(), align='left', color='cherry')
r.add_header('Columns <a href=www.google.com>www.google.com</a>', color='ocean')
r.add_text(lorem.paragraph(), align='center', color='ocean')
r.add_header('Columns <a href=www.google.com>www.google.com</a>', color='earth')
r.add_text(lorem.paragraph(), align='right', color='earth')
# The report class supports a variety of layout functions, which can also be nested. They all follow the same
# principle:
# 1) Open the layouter
# 2) Add layout item
# 3) Close layouter
# Here's a column layouter with three columns as an example:
r.add_header('Columns I, II, III', color='cappuccino')
r.open_columns()
r.add_column()
r.add_header('Column I')
r.add_text(lorem.paragraph(), align='left')
r.add_column()
r.add_header('Column II')
r.add_text(lorem.paragraph(), align='right')
r.add_column()
r.add_header('Column III')
r.add_text(lorem.paragraph())
r.close_columns()
# You might want to add a sub-section with a slightly smaller header. The level defines the width of the section
# header and ranges from 12 (widest header) to 1 (smallest header, which seems almost useless). The header will
# adjust to its surroundings. I.e. if you are already in a column layouter with 3 columns sub_level 1 will only span
# the column it is called in. You can further reduce the level of the content by putting it in a sublevel element
r.add_sub_header('Sub header of level 10', sub_level=10)
r.open_sublevel(size=10)
r.add_text(lorem.paragraph())
r.close_sublevel()
r.add_sub_header('Sub header of level 6', sub_level=6)
r.open_sublevel(size=6)
r.add_text(lorem.paragraph())
r.close_sublevel()
# If you want to add a table you can do that easily by calling add_table. The data can either be a pandas DataFrame
# or an array-like of array-likes. You can specify an array of formatters, one for every column. Or you can pass a
# single formatter that is used on every column. If you pass a pandas dataframe the index will be the first column
# and the formatter is automatically set to str. This default behavior might change in the future. If you pass a
# list of lists the first row represents the table headers, so the formatters won't be applied to them.
# There are some additional options you can enable for the table
# 1) search: Adds a search box to the table
# 2) page: Only shows 10 entries at a time (can be extended to 25, 50 or 100)
# 3) info: Simply adds a text to the bottom of the table highlighting how many entries are currently shown and
# how many there are in total
# 4) no_sort: Disables initial sorting
# 5) color_negative_values: Colors values below zero in red
# 6) color_positive_values: Colors values greater than zero in green
# 7) full_width: Use the full page width to display the table
# You can also add a caption to the table. And you can adjust the width of the table using `size` and the alignment
# of the cell content using `align`
r.add_header('Large Table (color=cherry)', color='cherry')
fmts = ['{:.1f}'.format, '{:2.0e}'.format, str, '{}'.format, '{:5.2e}'.format]
r.add_table(
table,
options=['search', 'page', 'info'],
formatters=fmts,
caption='This is an example caption of a searchable table',
)
# If no options are selected all data will be shown. So if you really like long tables you can still show them on
# the report page.
r.add_header('Smaller Table w/ alignment (color=sunrise)', color='sunrise')
r.add_table(
table.iloc[:5],
formatters='{:.2f}'.format,
caption='Small Table',
align='left',
size=8,
)
# Just like the column layouter the tab layouter needs to be opened and closed to work. Just like every other
# layouter it will automatically tell you to close the layout environment if you forget to do it. Once the tab
# layouter is open you can add individual tabs again. Everything following an add_tab method will be displayed
# within this particular tab
r.add_header('How to use tabs')
r.open_tabs()
with tempfile.TemporaryDirectory() as tmp_dir:
r.add_tab('Plot 1')
plt.figure(figsize=(12,8))
plt.plot(np.arange(100), np.random.rand(100))
out_file = Path(tmp_dir) / 'plot1.png'
plt.savefig(out_file)
r.add_image(out_file)
r.add_tab('Plot 2')
plt.figure(figsize=(12,8))
plt.plot(np.arange(100), np.random.rand(100))
out_file = Path(tmp_dir) / 'plot2.png'
plt.savefig(out_file)
r.add_image(out_file)
r.close_tabs()
# As detailed above, the navbar layouter works just like the tabs one with one exception: When opening the layouter
# you can actually select the location of the navbar pills, i.e., left, top, or right
r.add_header('How to use tabs')
r.add_header('Navbars')
r.open_navbar(loc='top')
r.add_navbar_item('Item 1')
r.add_text(lorem.paragraph(), align='left')
r.add_navbar_item('Item 2')
r.add_text(lorem.paragraph())
r.add_navbar_item('Item With A Long Name')
r.add_text(lorem.paragraph(), align='right')
r.close_navbar()
r.open_navbar(loc='left', color='ocean')
r.add_navbar_item('Item 1')
r.add_text(lorem.paragraph(), align='left')
r.add_navbar_item('Item 2')
r.add_text(lorem.paragraph())
r.add_navbar_item('Item With A Long Name')
r.add_text(lorem.paragraph(), align='right')
r.close_navbar()
r.open_navbar(loc='right', color='cherry')
r.add_navbar_item('Item 1')
r.add_text(lorem.paragraph(), align='left')
r.add_navbar_item('Item 2')
r.add_text(lorem.paragraph())
r.add_navbar_item('Item With A Long Name')
r.add_text(lorem.paragraph(), align='right')
r.close_navbar()
# Last but not least there are the accordions. They are again following the default layouter syntax
r.add_header('Accordions')
r.open_accordion()
r.add_accordion_item('Show Lorem')
r.add_text(lorem.paragraph())
r.add_accordion_item('Show Ipsum')
r.add_text(lorem.paragraph())
r.add_accordion_item('Show Dolor')
r.add_text(lorem.paragraph())
r.add_accordion_item('Show Table')
r.add_table(table, size=12)
r.close_accordion()
# You can also add new pages to the report. This will not automatically switch the reporter to the newly created
# page. So calling r.add_xyz still targets your old page. You can now either change the report to the new page or
# reference it via key
r.add_page('page2')
r['page2'].add_header('This is page 2')
r.set_current_page('page2')
r.add_text(lorem.paragraph())
r.add_text(lorem.paragraph(), align='left')
r.add_text(lorem.paragraph(), align='right')
# You can add links between pages either locally or globally. Local links will appear where you add them. They are
# plain old vanilla html links. Because of the way the reporter is set up this only works calling the reporter
# object and it does not work calling the keyed page as the page object does not know about the other pages.
r.add_local_link_to_page('main', 'Local link to main')
# Global links on the other hand are listed in a toolbar right on top of the page. They are visible for all
# subpages. You also have to include main in your link list as we don't include it as a default. Global links can
# be added from any page as they will be added to every page at the end.
r.add_global_link_to_page('main', 'This is a global link to main')
r.add_global_link_to_page('page2', 'This is a global link to subpage 2')
# And let's add another page
r.add_page('page3')
r.add_global_link_to_page('page3', 'Page 3')
r['page3'].add_table(table)
# Actually write the whole report to file
r.dump()
if __name__ == '__main__':
make_report()