Open jrycw opened 3 months ago
I submitted a color picker example to FastHTML-Gallery
to demonstrate how to integrate Great Tables
with FastHTML
here.
The maintainer suggested that we could create a plugin to help users more easily adopt Great Tables
in a FastHTML app.
I've drafted a rough implementation at the end of this message. I created a decorator called gt2fasthtml
that allows users to return a GT
instance, with the output of GT.as_raw_html()
wrapped in FastHTML
components.
The implementation seems straightforward, and we could consider adding a decorators.py
file to include various decorators for different apps. For more complex apps like Django
, users might need a separate plugin or template tag to achieve this. However, for apps primarily designed for data scientists, the decorators.py
file could meet their needs without requiring an additional plugin. Which approach do you prefer?
from functools import cache, partial, wraps
import polars as pl
from fasthtml.common import (
H2,
Card,
Div,
Form,
Grid,
Input,
Main,
NotStr,
Title,
Titled,
fast_app,
)
from great_tables import GT, html
from great_tables.data import sza
app, rt = fast_app()
def gt2fasthtml(func=None, **div_kwargs):
"""
https://pybit.es/articles/decorator-optional-argument/
"""
if func is None:
return partial(gt2fasthtml, **div_kwargs)
@wraps(func)
def wrapper(*args, **kwargs):
gtbl = func(*args, **kwargs)
gtbl_html = gtbl.as_raw_html()
return Div(NotStr(gtbl_html), **div_kwargs)
return wrapper
@cache
def get_sza_pivot():
return (
pl.from_pandas(sza)
.filter((pl.col("latitude") == "20") & (pl.col("tst") <= "1200"))
.select(pl.col("*").exclude("latitude"))
.drop_nulls()
.pivot(values="sza", index="month", on="tst", sort_columns=True)
)
@gt2fasthtml(id="gt")
def get_gtbl(color1: str = "#663399", color2: str = "#FFA500"):
return (
GT(get_sza_pivot(), rowname_col="month")
.data_color(
domain=[90, 0],
palette=[color1, "white", color2],
na_color="white",
)
.tab_header(
title="Solar Zenith Angles from 05:30 to 12:00",
subtitle=html("Average monthly values at latitude of 20°N."),
)
.sub_missing(missing_text="")
)
@app.post("/submit")
def post(d: dict):
return get_gtbl(**d)
@app.get("/")
def homepage():
return (
Title("FastHTML-GT Website"),
Titled("Great Tables shown in FastHTML", style="text-align:center"),
Main(
Form(
hx_post="/submit",
hx_target="#gt",
hx_trigger="input",
hx_swap="outerHTML",
)(
Grid(
Div(),
Card(
H2("Color1"), Input(type="color",
id="color1", value="#663399")
),
Card(
H2("Color2"), Input(type="color",
id="color2", value="#FFA500")
),
Div(),
)
),
get_gtbl(),
cls="container",
),
)
For the Streamlit
app, we could introduce a gt2streamlit
decorator that automatically calls GT.as_raw_html()
and sets unsafe_allow_html=False
, similar to the code at the end. I'm not sure if this approach will be well-received, but since users seem to be confused about how to render our tables, I reckon this decorator could reduce some of the friction for them.
from functools import wraps
import polars as pl
import streamlit as st
from great_tables import GT, html
from great_tables.data import sza
def gt2streamlit(func):
@wraps(func)
def wrapper(*args, **kwargs):
gtbl = func(*args, **kwargs)
gtbl_html = gtbl.as_raw_html()
return st.write(gtbl_html, unsafe_allow_html=True)
return wrapper
@st.cache_data
def get_sza():
return pl.from_pandas(sza)
@gt2streamlit
def st_write_gt(df):
sza_pivot = (
df
.filter((pl.col("latitude") == "20") & (pl.col("tst") <= "1200"))
.select(pl.col("*").exclude("latitude"))
.drop_nulls()
.pivot(values="sza", index="month", on="tst", sort_columns=True)
)
return (
GT(sza_pivot, rowname_col="month")
.data_color(
domain=[90, 0],
palette=["rebeccapurple", "white", "orange"],
na_color="white",
)
.tab_header(
title="Solar Zenith Angles from 05:30 to 12:00",
subtitle=html("Average monthly values at latitude of 20°N."),
)
.sub_missing(missing_text="")
)
st.title("Great Tables shown in Streamlit")
st_write_gt(get_sza())
Hello @rich-iannone and @machow ,
I've noticed that some users have questions about how to integrate
great-tables
into their applications. The requests come from various frameworks, including Streamlit, Panel, FastAPI, Flask, and Django, among others. While it's not feasible for us to cover every possible use case, I think it would be beneficial to create a section in the documentation (or a separate demo repository) that demonstrates how to render tables in raw HTML within these applications.I've drafted four examples using Streamlit, FastHTML, FastAPI and Django. Regarding the Panel app, Marc Skov Madsen has already created a great demonstration here. What do you think of this approach?