Skip to content
Open
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
52 changes: 37 additions & 15 deletions lumen/ai/editors.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,20 @@
from .report import Task


_PAGINATED_TABLE_STYLES = """
/* Tabulator hides page numbers when its footer overflows horizontally. */
.tabulator .tabulator-footer .tabulator-paginator {
display: flex !important;
flex-wrap: wrap !important;
justify-content: flex-end !important;
}
.tabulator .tabulator-footer .tabulator-pages {
display: inline-flex !important;
flex-wrap: wrap !important;
}
"""


class LumenEditor(Viewer):

component = param.ClassSelector(class_=Component)
Expand Down Expand Up @@ -207,18 +221,12 @@ def _update_component(self):
async def _render_pipeline(self, pipeline):
table = Table(
pipeline=pipeline, pagination='remote',
min_height=200, sizing_mode="stretch_both", stylesheets=[
"""
.tabulator-footer {
display: flex;
text-align: left;
padding: 0px;
}
"""
]
page_size=10, min_height=200, sizing_mode="stretch_both",
stylesheets=[_PAGINATED_TABLE_STYLES],
)
controls = Row(
styles={'position': 'absolute', 'right': '40px', 'top': '-35px'}
layout = Column(
table,
css_classes=['full-data-table-container'],
)
for sql_limit in pipeline.sql_transforms:
if isinstance(sql_limit, SQLLimit):
Expand All @@ -229,14 +237,28 @@ async def _render_pipeline(self, pipeline):
data = as_pandas(pipeline.data)
limited = len(data) == sql_limit.limit
if limited:
limited_limit = sql_limit.limit

controls = Row(
height=36, align='end', sizing_mode='stretch_width',
css_classes=['full-data-controls'],
styles={
'justify-content': 'flex-end',
'align-items': 'center',
},
)

def unlimit(e):
sql_limit.limit = None if e.new else 1_000_000
sql_limit.limit = None if e.new else limited_limit

full_data = Checkbox(
label='Full data', width=100, visible=limited
label='Full data', css_classes=['full-data-checkbox'],
height=36, margin=0, visible=limited
)
full_data.param.watch(unlimit, 'value')
controls.insert(0, full_data)
return Column(controls, table)
controls.append(full_data)
layout.append(controls)
return layout

async def render_context(self):
view = self.component
Expand Down
67 changes: 67 additions & 0 deletions lumen/tests/ai/test_editors.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from lumen.base import Component
from lumen.pipeline import Pipeline
from lumen.sources.duckdb import DuckDBSource
from lumen.transforms.sql import SQLLimit


class MockComponent(Component):
Expand Down Expand Up @@ -202,6 +203,72 @@ def test_render_controls_inserts_add_filter_menu(sql_pipeline_editor):
assert "Add Filter" in labels


@pytest.fixture
def limited_sql_pipeline_editor(monkeypatch):
"""Return an editor whose source data exceeds its SQL limit."""
source = DuckDBSource(tables={
'tiny': """
SELECT * FROM (
VALUES (1,'A'),
(2,'B'),
(3,'C'),
(4,'D')
) AS t(id, category)
"""
})
pipeline = Pipeline(
source=source, table='tiny', sql_transforms=[SQLLimit(limit=3)]
)
monkeypatch.setattr(editors_module, 'ParamMethod', lambda *args, **kwargs: None)
return SQLEditor(component=pipeline, spec="SELECT * FROM tiny")


@pytest.mark.asyncio
async def test_render_pipeline_places_full_data_control_below_table(limited_sql_pipeline_editor):
editor = limited_sql_pipeline_editor
assert len(editor.component.data) == 3

layout = await editor._render_pipeline(editor.component)
table, controls = layout.objects

assert layout.css_classes == ['full-data-table-container']
assert table._pane.page_size == 10
assert controls.sizing_mode == 'stretch_width'
assert controls.height == 36
assert controls.align == 'end'
assert controls.margin == 0
assert controls.styles == {
'justify-content': 'flex-end',
'align-items': 'center',
}
assert table._pane.stylesheets == [editors_module._PAGINATED_TABLE_STYLES]
assert not controls.stylesheets
full_data = controls.objects[0]
assert full_data.label == "Full data"
assert full_data.width is None
assert full_data.height == 36
assert full_data.css_classes == ['full-data-checkbox']
assert full_data.margin == 0
assert full_data.visible

full_data.value = True
assert editor.component.sql_transforms[0].limit is None
assert len(editor.component.data) == 4

full_data.value = False
assert editor.component.sql_transforms[0].limit == 3
assert len(editor.component.data) == 3


@pytest.mark.asyncio
async def test_render_pipeline_omits_empty_full_data_row(sql_pipeline_editor):
layout = await sql_pipeline_editor._render_pipeline(sql_pipeline_editor.component)

assert layout.css_classes == ['full-data-table-container']
assert len(layout.objects) == 1
assert layout.objects[0]._pane.page_size == 10


@pytest.fixture
def xarray_pipeline_editor(monkeypatch):
"""SQLEditor over an xarray source so coordinate dimensions are exercised."""
Expand Down