Rico
A Python package for creating HTML documents from rich content: dataframes, plots, images, markdown etc. It provides a high-level, easy-to-use API with reasonable defaults, as well as low-level access for better control.
Install / Use
/learn @e10v/RicoREADME
rico: rich content to HTML as easy as Doc(df, plot)
rico is a Python package for creating HTML documents from rich content: dataframes, plots, images, markdown etc. It provides a high-level, easy-to-use API with reasonable defaults, as well as low-level access for better control.
Use rico if you want to create an HTML document from objects created in a Python script.
With rico you can avoid:
- Writing data to intermediate files or a database from a script.
- Loading data into a Jupyter notebook from intermediate files or a database.
- Using nbconvert or similar tools for creating HTML files.
Learn more about why rico was created with a quick start guide: https://e10v.me/rico-rich-content-to-html-easy/
Installation
pip install rico
rico has no dependencies other than standard Python packages.
For Markdown support:
- install markdown-it-py,
- or install Python Markdown,
- or set your own Markdown renderer using
rico.set_config.
User guide
To get started with rico, take a look at the self-explanatory examples with resulting HTML documents. The user guide contains a slightly more detailed explanation.
Basic usage
rico provides both declarative and imperative style interfaces.
Declarative style:
import pandas as pd
import rico
df = pd.DataFrame(
{
"x": [2, 7, 4, 1, 2, 6, 8, 4, 7],
"y": [1, 9, 2, 8, 3, 7, 4, 6, 5],
},
index=pd.Index(list("AAABBBCCC")),
)
plot = df.plot.scatter(x="x", y="y")
doc = rico.Doc("Hello, World!", df, plot, title="My doc")
The result:
<div align="center"> <a href="https://raw.githubusercontent.com/e10v/rico/main/images/basic_usage.png" target="_blank"> <img src="https://raw.githubusercontent.com/e10v/rico/main/images/basic_usage.png" width="480px"> </a> </div>Imperative style:
doc = rico.Doc(title="My doc")
doc.append("Hello, World!", df, plot)
Also imperative style:
doc = rico.Doc(title="My doc")
doc.append("Hello, World!")
doc.append(df)
doc.append(plot)
Mix-and-match:
doc = rico.Doc("Hello, World!", df, title="My doc")
doc.append(plot)
Serialization
Serialize the document to HTML using str(doc):
with open("doc.html", "w") as f:
f.write(str(doc))
Implicit serialization:
with open("doc.html", "w") as f:
print(doc, file=f)
Internally, str(doc) calls doc.serialize() with default parameter values. Call doc.serialize(indent=True) to indent the HTML element tree visually:
with open("doc.html", "w") as f:
f.write(doc.serialize(indent=True))
Set custom whitespace for indentation using the space parameter:
with open("doc.html", "w") as f:
f.write(doc.serialize(indent=True, space=" "))
Remove unnecessary whitespace by setting strip to True:
with open("doc.html", "w") as f:
f.write(doc.serialize(strip=True))
Control the default behavior of str(doc) and doc.serialize() using the global options indent_html, indent_space, and strip_html:
with open("doc.html", "w") as f, rico.config_context(indent_html=True):
f.write(str(doc))
The default option values are:
indent_html = False,indent_space = " ",strip_html = False.
Rich content types
rico automatically recognizes the following content types:
ricocontent classes (subclasses ofrico.ContentBase).- Matplotlib Pyplot Plots.
- Dataframes and other types with IPython rich representation methods.
- Text.
Use specific classes for plots and text to change the default behavior:
doc = rico.Doc(
rico.Text("Hello, World!", mono=True), # The default value is False.
df,
rico.Plot(plot, format="png", bbox_inches="tight"), # The default value is "svg".
title="My doc",
)
The following code gives the same result as the code above:
doc = rico.Doc(title="My doc")
doc.append_text("Hello, World!", mono=True)
doc.append(df)
doc.append_plot(plot, format="png", bbox_inches="tight")
Some options can be set in the global configuration:
with rico.config_context(text_mono=True, image_format="png"):
doc = rico.Doc("Hello, World!", df, plot, title="My doc")
Use specific classes and methods for other content types:
- Images:
ImageorDoc.append_image. - Code:
CodeorDoc.append_code. - Markdown*:
MarkdownorDoc.append_markdown. - HTML tag:
TagorDoc.append_tag. - Raw HTML:
HTMLorDoc.append_html.
*Install markdown-it-py or markdown, or define a custom Markdown renderer with the markdown_renderer global option to use Markdown or Doc.append_markdown.
For example:
doc = rico.Doc(
rico.Markdown("## Dataframe"),
df,
rico.Tag("h2", "Plot"), # An alternative way to add a header.
plot,
rico.HTML("<h2>Code</h2>"), # Another way to add a header.
rico.Code("print('Hello, World!')"),
title="My doc",
)
The result:
<div align="center"> <a href="https://raw.githubusercontent.com/e10v/rico/main/images/content_types.png" target="_blank"> <img src="https://raw.githubusercontent.com/e10v/rico/main/images/content_types.png" width="480px"> </a> </div>The following code gives the same result as the code above:
doc = rico.Doc(title="My doc")
doc.append_markdown("## Dataframe")
doc.append(df)
doc.append_tag("h2", "Plot")
doc.append(plot)
doc.append_html("<h2>Code</h2>")
doc.append_code("print('Hello, World!')")
Check the docstrings for details.
Serialize content to HTML using str(object) or object.serialize():
obj = rico.Tag("p", "Hello, World!")
print(obj)
# <div><p>Hello, World!</p></div>
print(obj.serialize(indent=True, space=" "))
# <div>
# <p>Hello, World!</p>
# </div>
Bootstrap, HTML classes and document layout
By default, Bootstrap styles are included in the document. The resulting documents are responsive and mobile-friendly. Change the default behavior using the bootstrap parameter:
doc = rico.Doc("Hello, World!", bootstrap="full")
- Set
bootstrapto"css"(default) to include only CSS. - Set
bootstrapto"full"to include both the CSS and JS. - Set
bootstrapto"none"to not include Bootstrap*.
*Keep in mind that rico relies on Bootstrap classes and styles. For example:
- The
monoandwrapparameters of theTextclass use Bootstrap'sfont-monospaceandfont-monospaceclasses. - rico's dataframe style definition uses Bootstrap variables.
Each content element is wrapped in a <div> container. Specify the element's container class using the class_ parameter:
print(rico.Tag("p", "Hello, World!", class_="col"))
# <div class="col"><p>Hello, World!</p></div>
All elements' containers in the document are also wrapped in a <div> container. Specify the document's container class using the class_ parameter:
doc = rico.Doc("Hello, World!", class_="container-fluid")
Define the document layout using Bootstrap and Div class:
doc = rico.Doc(rico.Div(
rico.Obj(df, class_="col"),
rico.Obj(plot, class_="col"),
class_="row row-cols-auto",
))
The code above creates a document with two columns, one with a dataframe and another with a plot. The Obj is a magic class which automatically determines the content type in the same way that Doc and Doc.append do.
Another example:
import altair as alt
doc = rico.Doc(
rico.Tag("h2", "Dataframes"),
rico.Div(
rico.Obj(rico.Tag("h3", "A"), df.loc["A", :], class_="col"),
rico.Obj(rico.Tag("h3", "B"), df.loc["B", :], class_="col"),
rico.Obj(rico.Tag("h3", "C"), df.loc["C", :], class_="col"),
class_="row row-cols-auto",
),
rico.Tag("h2", "Plots"),
rico.Div(
rico.Obj(
rico.Tag("h3", "A"),
alt.Chart(df.loc["A", :]).mark_point().encode(x="x", y="y"),
class_="col",
),
rico.Obj(
rico.Tag("h3", "B"),
alt.Chart(df.loc["B", :]).mark_point().encode(x="x", y="y"),
class_="col",
),
rico.Obj(
rico.Tag("h3", "C"),
alt.Chart(df.loc["C", :]).mark_point().encode(x="x", y="y"),
class_="col",
),
class_="row row-cols-auto",
),
title="Grid system",
)
The result:
<div align="center"> <a href="https://raw.githubusercontent.com/e10v/rico/main/images/layout.png" target="_blank"> <img src="https://raw.githubusercontent.com/e10v/rico/main/images/layout.png" width="720px"> </a> </div>The following code gives the same result as the code above:
doc = rico.Doc(title="Grid system")
doc.append_tag("h2", "Dataframes")
div1 = rico.Div(class_="row row-cols-auto")
doc.append(div1)
for name, data in df.groupby(df.index):
div1.append(rico.Tag("h3", name), data, class_="col")
doc.append_tag("h2", "Plots")
div2 =
