Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More information in the loading message #258

Merged
merged 8 commits into from
Apr 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading
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
Loading