Skip to content

Commit

Permalink
More information in the loading message (#258)
Browse files Browse the repository at this point in the history
  • Loading branch information
mwouts committed Apr 24, 2024
1 parent 3f8e8bd commit ddf7ff9
Show file tree
Hide file tree
Showing 9 changed files with 208 additions and 75 deletions.
53 changes: 0 additions & 53 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,56 +66,3 @@ execute `init_notebook_mode`.
- PyCharm (for Jupyter Notebooks)
- Quarto
- Shiny for Python

## Try ITables on Binder

You can run our examples notebooks directly on [![Lab](https://img.shields.io/badge/Binder-JupyterLab-blue.svg)](https://mybinder.org/v2/gh/mwouts/itables/main?urlpath=lab/tree/docs/quick_start.md), without having to install anything on your side.

## Table not loading?

If the table just says "Loading...", then maybe
- You loaded a notebook that is not trusted (run "Trust Notebook" in View / Activate Command Palette)
- You forgot to run `init_notebook_mode`, or you deleted that cell or its output
- Or you ran `init_notebook_mode(connected=True)` but you are not connected to the internet?

Please note that if you change the value of the `connected` argument in
the `init_notebook_mode` cell, you will need to re-execute all the cells
that display interactive tables.

If the above does not help, please check out the [ChangeLog](docs/changelog.md)
and decide whether you should upgrade `itables`.

## <a name="downsampling"></a> Downsampling

When the data in a table is larger than `maxBytes`, which is equal to 64KB by default, `itables` will display only a subset of the table - one that fits into `maxBytes`. If you wish, you can deactivate the limit with `maxBytes=0`, change the value of `maxBytes`, or similarly set a limit on the number of rows (`maxRows`, defaults to 0) or columns (`maxColumns`, defaults to `pd.get_option('display.max_columns')`).

Note that DataTables support [server-side processing](https://datatables.net/examples/data_sources/server_side). At a later stage we may implement support for larger tables using this feature.

```{code-cell}
from itables.sample_dfs import get_indicators
from itables.downsample import nbytes
import itables.options as opt
opt.lengthMenu = [2, 5, 10, 20, 50, 100, 200, 500]
opt.maxBytes = 10000
df = get_indicators()
nbytes(df)
```

```{code-cell}
df
```

To show the table in full, we can modify the value of `maxBytes` either locally:

```{code-cell}
show(df, maxBytes=0)
```

or globally:

```{code-cell}
opt.maxBytes = 2 ** 20
df
```
8 changes: 8 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
ITables ChangeLog
=================

2.0.1-dev (2024-04-??)
------------------

**Added**
- We have added a logo for ITables ([#257](https://github.com/mwouts/itables/issues/257))
- The _loading_ message gives more information, including the version of ITables and where DataTables is loaded from ([#258](https://github.com/mwouts/itables/issues/258))


2.0.0 (2024-03-16)
------------------

Expand Down
57 changes: 51 additions & 6 deletions docs/troubleshooting.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,58 @@
---
jupytext:
formats: md:myst
text_representation:
extension: .md
format_name: myst
format_version: 0.13
jupytext_version: 1.14.5
kernelspec:
display_name: itables
language: python
name: itables
---

# Troubleshooting

If the table just says "Loading...", then maybe
- You loaded a notebook that is not trusted (run "Trust Notebook" in View / Activate Command Palette)
- You forgot to run `init_notebook_mode`, or you deleted that cell or its output
```{code-cell}
:tags: [hide-input]
import pandas as pd
from itables import show
df = pd.DataFrame()
tags = (
'<caption style="caption-side: bottom">A table that does not load, due '
"to <code>init_notebook_mode</code><br>not being called in this document</caption>"
)
show(df, connected=False, tags=tags)
```

If a table says "Loading..." forever, then maybe
- You forgot to run `init_notebook_mode` (like in the example above), or you deleted that cell or its output
- Or you ran `init_notebook_mode(connected=True)` but you are not connected to the internet?

Please note that if you change the value of the `connected` argument in
the `init_notebook_mode` cell, you will need to re-execute all the cells
```{tip}
If you change the value of the `connected` argument in
the `init_notebook_mode` cell, you need to re-execute all the cells
that display interactive tables.
```

It could also be that your notebook is not _trusted_. This happens when you
have not run the notebook in full yourself (e.g. the notebook was sent to you with outputs,
or the notebook was created by a tool like `papermill`). In that case, JavaScript
code cannot run (and the interactive tables won't display)
until you tell Jupyter that you trust the notebook content
(run "Trust Notebook" in View / Activate Command Palette).

If the above does not help, please check out the [ChangeLog](changelog.md)
and decide whether you should upgrade `itables`.
and decide whether you should upgrade `itables`. You can tell the version
of ITables that you are using by looking at the loading message (from ITables v2.0.1 on)
or by running this code snippet:
```python
import itables as it

it.__version__
```
46 changes: 40 additions & 6 deletions itables/javascript.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,19 +135,39 @@ def init_notebook_mode(
display(HTML(generate_init_offline_itables_html(dt_bundle)))


def get_animated_logo():
if not opt.display_logo_when_loading:
return ""
return f"""<div style="float:left; margin-right: 10px;">
<a href=https://mwouts.github.io/itables/>{read_package_file("logo/loading.svg")}</a>
</div>
"""


def generate_init_offline_itables_html(dt_bundle: Path):
assert dt_bundle.suffix == ".js"
dt_src = dt_bundle.read_text()
dt_css = dt_bundle.with_suffix(".css").read_text()
dt64 = b64encode(dt_src.encode("utf-8")).decode("ascii")

id = "itables_init_notebook_mode_" + str(uuid.uuid4()).replace("-", "_")

return f"""<style>{dt_css}</style>
<script>window.{DATATABLES_SRC_FOR_ITABLES} = "data:text/javascript;base64,{dt64}"</script>
<div id="{id}" style="vertical-align:middle; text-align:left">
{get_animated_logo()}<div>
This is the <code>init_notebook_mode</code> cell from ITables v{itables_version}<br>
(you should not see this message - is your notebook <it>trusted</it>?)
</div>
</div>
<script>
window.{DATATABLES_SRC_FOR_ITABLES} = "data:text/javascript;base64,{dt64}";
document.querySelectorAll("#{id}").forEach(e => e.remove());
</script>
"""


def _table_header(
df, table_id, show_index, classes, style, tags, footer, column_filters
df, table_id, show_index, classes, style, tags, footer, column_filters, connected
):
"""This function returns the HTML table header. Rows are not included."""
# Generate table head using pandas.to_html(), see issue 63
Expand All @@ -163,8 +183,16 @@ def _table_header(
if not show_index and len(df.columns):
thead = thead.replace("<th></th>", "", 1)

loading = "<td>Loading... (need <a href=https://mwouts.github.io/itables/troubleshooting.html>help</a>?)</td>"
tbody = "<tr>{}</tr>".format(loading)
itables_source = (
"the internet" if connected else "the <code>init_notebook_mode</code> cell"
)
tbody = f"""<tr>
<td style="vertical-align:middle; text-align:left">
{get_animated_logo()}<div>
Loading ITables v{itables_version} from {itables_source}...
(need <a href=https://mwouts.github.io/itables/troubleshooting.html>help</a>?)</td>
</div>
</tr>"""

if style:
style = 'style="{}"'.format(style)
Expand Down Expand Up @@ -412,7 +440,7 @@ def filter_control(control):
pass

table_header = _table_header(
df, tableId, showIndex, classes, style, tags, footer, column_filters
df, tableId, showIndex, classes, style, tags, footer, column_filters, connected
)

# Export the table data to JSON and include this in the HTML
Expand Down Expand Up @@ -472,7 +500,13 @@ def set_default_options(kwargs, use_to_html):
(not use_to_html or (option not in _OPTIONS_NOT_AVAILABLE_WITH_TO_HTML))
and option not in kwargs
and not option.startswith("__")
and option not in {"dt_bundle", "find_package_file", "UNPKG_DT_BUNDLE_URL"}
and option
not in {
"dt_bundle",
"find_package_file",
"display_logo_when_loading",
"UNPKG_DT_BUNDLE_URL",
}
):
kwargs[option] = getattr(opt, option)

Expand Down
89 changes: 89 additions & 0 deletions itables/logo/loading.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions itables/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,6 @@

"""The DataTable bundle for the offline mode"""
dt_bundle = find_package_file("dt_for_itables/dt_bundle.js")

"""Display the ITables animated logo when loading"""
display_logo_when_loading = True
2 changes: 1 addition & 1 deletion itables/version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""ITables' version number"""

__version__ = "2.0.0"
__version__ = "2.0.1-dev"
24 changes: 15 additions & 9 deletions tests/test_connected_notebook_is_small.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
import pytest
from jupytext.cli import jupytext


def text_notebook(connected):
return """# %%
def text_notebook(connected, display_logo_when_loading=True):
return f"""# %%
from itables import init_notebook_mode
import itables.options as opt
init_notebook_mode(all_interactive=True, connected={})
opt.display_logo_when_loading = {display_logo_when_loading}
init_notebook_mode(all_interactive=True, connected={connected})
# %%
import pandas as pd
pd.DataFrame()
""".format(
connected
)
"""


def test_connected_notebook_is_small(tmp_path):
@pytest.mark.parametrize("display_logo_when_loading", [True, False])
def test_connected_notebook_is_small(tmp_path, display_logo_when_loading):
nb_py = tmp_path / "nb.py"
nb_ipynb = tmp_path / "nb.ipynb"
nb_py.write_text(text_notebook(connected=True))
nb_py.write_text(
text_notebook(
connected=True, display_logo_when_loading=display_logo_when_loading
)
)
jupytext([str(nb_py), "--to", "ipynb", "--set-kernel", "itables", "--execute"])
assert nb_ipynb.exists()
assert nb_ipynb.stat().st_size < 5000
assert nb_ipynb.stat().st_size < (8000 if display_logo_when_loading else 5000)


def test_offline_notebook_is_not_too_large(tmp_path):
Expand Down
1 change: 1 addition & 0 deletions tests/test_datatables_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ def test_datatables_rows(df, expected):
tags="",
footer=False,
column_filters=False,
connected=False,
)
column_count = _column_count_in_header(table_header)
actual = datatables_rows(df, count=column_count)
Expand Down

0 comments on commit ddf7ff9

Please sign in to comment.