Use of pn.panel.HTML overwrites jupyterlab top bar

I’m creating a simple version of awesome-panel/pandas_profiling_app.py at master · MarcSkovMadsen/awesome-panel · GitHub

I’m using pn.panel.HTML to display the html report (in a jupyterlab notebook (where i’m doing my development it overwrites the top bar)). However, calling pn.pane.HTML(profile_html) overwrite the top bar of my jupyterlab.

Is pn.pane.HTML(profile_html) the correct command to use? Should it be wrapped in something else?

Code:

import pandas as pd
import panel as pn
from pandas_profiling import ProfileReport

pn.extension()

df = pd.read_csv(
    "https://raw.githubusercontent.com/MarcSkovMadsen/awesome-panel/master/application/pages/awesome_panel_express_tests/PerspectiveViewerData.csv"
)
profile = ProfileReport(df, title="title", minimal=False)
profile_html = profile.to_html()
pn.pane.HTML(profile_html)

Note: i’ll eventually be deploying this via cdsdasboards in jupyterhub.

Maybe I’m missing something, but can’t you just output profile directly?

If you must use pn.pane.HTML you can do something like what @Marc did and wrap it into an iframe.

import html

html_report = html.escape(profile.to_html())
pn.pane.HTML(f"""<iframe style="width: 100%; height: 710px; overflow: auto;" frameborder=0 srcdoc="{html_report}" """, sizing_mode="stretch_width")

1 Like

Maybe I’m missing something, but can’t you just output profile directly?

Yes, but how you would you embed it into a panel app?

app.py:

import pandas as pd
from pandas_profiling import ProfileReport

df = pd.read_csv(
    "https://raw.githubusercontent.com/MarcSkovMadsen/awesome-panel/master/application/pages/awesome_panel_express_tests/PerspectiveViewerData.csv"
)
ProfileReport(df, title="title", minimal=False)

$ panel serve app.py

The jupyterlab header issue goes away when you close the notebook.

Thanks. I’ll take a look at the iframe approach

The iframe option is definitely the best approach:

import pandas as pd
from pandas_profiling import ProfileReport
from pandas_profiling.report.presentation.flavours.widget.notebook import get_notebook_iframe

df = pd.read_csv(
    "https://raw.githubusercontent.com/MarcSkovMadsen/awesome-panel/master/application/pages/awesome_panel_express_tests/PerspectiveViewerData.csv"
)
report = ProfileReport(df, title="title", minimal=False)

pn.pane.HTML(get_notebook_iframe(report.config, report), sizing_mode='stretch_width')

If you were to submit a PR containing this so that Panel knows how to render a Pandas Profiling report I’d happily accept it:

from panel.pane.markup import HTML, DivPaneBase, escape

class ProfileReport(HTML):

    @classmethod
    def applies(cls, obj):
        module = getattr(obj, '__module__', '')
        name = type(obj).__name__
        if module.startswith('pandas_profiling') and name == 'ProfileReport':
            return 0.3
        else:
            return False
        
    def _get_properties(self):
        from pandas_profiling.report.presentation.flavours.widget.notebook import get_notebook_iframe
        properties = DivPaneBase._get_properties(self)
        html = '' if self.object is None else get_notebook_iframe(self.object.config, self.object)._repr_html_()
        return dict(properties, text=escape(html))
3 Likes

You made it before me @philippjfr . Would you accept PRs for other kinds of Auto Generated Data Reports? For example Lux?

My take on a solution would be this one.

pandas-profile-report2-speedup

import html

import pandas as pd
import panel as pn
from pandas_profiling import ProfileReport

pn.extension(sizing_mode="stretch_width", template="fast")


ACCENT_COLOR = "#FAAA8D"

def to_pandas_profile_pane(profile: ProfileReport, height=700, **params) -> pn.pane.HTML:
    profile_html = profile.to_html()
    html_report = html.escape(profile_html)
    return pn.pane.HTML(
        f"""<iframe style="width: 100%; height: {height}px; overflow: auto;" frameborder=0 srcdoc="{html_report}"></iframe>""",
        **params,
    )

if not "profile" in pn.state.cache:
    data = pd.read_csv(
        "https://raw.githubusercontent.com/MarcSkovMadsen/awesome-panel/master/application/pages/awesome_panel_express_tests/PerspectiveViewerData.csv"
    )
    profile = pn.state.cache["profile"] = ProfileReport(data, title="title", minimal=False)
else:
    profile = pn.state.cache["profile"]

to_pandas_profile_pane(profile).servable()

pn.state.template.param.update(
    site="Awesome Panel",
    title="Pandas Profile Report",
    accent_base_color=ACCENT_COLOR,
    header_background=ACCENT_COLOR,
)
2 Likes

Thanks for all the info here.

For performance reasons i’m starting with the html file after saving it in another script.

Code works well now:

app.ipynb:

import panel as pn
import s3fs

pn.extension(sizing_mode="stretch_width")

fs = s3fs.S3FileSystem()

with fs.open(
    f"s3://BUCKET/profile_report_escaped.html",
    "r",
) as f:
    profile_html_escaped = f.read()

pn.pane.HTML(
    f"""<iframe style="width: 100%; height: 700px; overflow: auto;" frameborder=0 srcdoc="{profile_html_escaped}"></iframe>""",
)

or
app.py:

import panel as pn
import s3fs

pn.extension(sizing_mode="stretch_width")

fs = s3fs.S3FileSystem()

with fs.open(
    f"s3://BUCKET/profile_report_escaped.html",
    "r",
) as f:
    profile_html_escaped = f.read()

pn.pane.HTML(
    f"""<iframe style="width: 100%; height: 700px; overflow: auto;" frameborder=0 srcdoc="{profile_html_escaped}"></iframe>""",
).servable()
2 Likes