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

Preview dataset #1288

Merged
merged 47 commits into from
Mar 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
007f7c2
set up preview in response and flowchart.py
Huongg Mar 13, 2023
0ff46cf
return preview for metaData
Huongg Mar 13, 2023
e469560
filter data and render the table
Huongg Mar 14, 2023
38e641c
add preview-table component
Huongg Mar 15, 2023
4fd5136
fix styling for table in metadata panel
Huongg Mar 15, 2023
6ca4ec4
add previewTable to metadata-modal
Huongg Mar 15, 2023
36931d0
different theme for preview table
Huongg Mar 15, 2023
2abe3a6
add classname for preview-table
Huongg Mar 15, 2023
85dda7c
add size for PreviewTable
Huongg Mar 16, 2023
6aa6b6e
add cursor pointer
Huongg Mar 16, 2023
9a6eae7
styling for row hover state
Huongg Mar 16, 2023
dc19dd1
light and dark theme for hovering
Huongg Mar 17, 2023
d41b4f6
include excel and parquet dataset
Huongg Mar 17, 2023
5d775cf
Merge branch 'main' of github.com:kedro-org/kedro-viz into preview-da…
Huongg Mar 17, 2023
6e143f9
add margin for table large
Huongg Mar 17, 2023
de652bd
check if self.type is in preview_datasets
Huongg Mar 17, 2023
20bcefd
remove preview table style
Huongg Mar 17, 2023
6a90f53
add preview text
Huongg Mar 17, 2023
a7c07b0
Adding test for preview table
Huongg Mar 17, 2023
e7b1a56
adding condition to not call _preview if not existed
Huongg Mar 20, 2023
2ce39d0
move the hasAttr inside the previewDataSet loops
Huongg Mar 20, 2023
c6ec97a
update light theme colour for highlighting
Huongg Mar 20, 2023
0a6c107
onclick handler when clicking on small table
Huongg Mar 20, 2023
4c4a2a1
update release note
Huongg Mar 20, 2023
7990f5a
remove highlighting row in small table
Huongg Mar 20, 2023
3d1de36
update previewing message
Huongg Mar 20, 2023
1f578a5
update variable names
Huongg Mar 20, 2023
e5fab1d
pull changes from remote branch
Huongg Mar 20, 2023
15e61d4
Merge branch 'main' into preview-dataset
Huongg Mar 20, 2023
9869229
ignore updateContent file
Huongg Mar 21, 2023
bc7a12c
formatting
Huongg Mar 21, 2023
a772729
test_data_node_metadata_preview for flowchart.py
Huongg Mar 21, 2023
ed13b44
remove parquet dataset preview
Huongg Mar 21, 2023
bc25c3e
restructure data in preview table
Huongg Mar 22, 2023
e446fdd
update name to onExpandMetaDataClick
Huongg Mar 22, 2023
c726a7b
restructure the flowchart.py for _preview
Huongg Mar 22, 2023
36b7c66
update test to reflect the new data structure
Huongg Mar 22, 2023
3658109
adding test for flowchart.py
Huongg Mar 22, 2023
538cf9c
downgrade mypy package
Huongg Mar 22, 2023
63b0b7d
formatting
Huongg Mar 22, 2023
4f558c7
include # pylint: disable=attr-defined for _preview
Huongg Mar 22, 2023
8d5ed2a
include # type: ignore
Huongg Mar 22, 2023
1244076
include ignore in response
Huongg Mar 22, 2023
e3f7924
update wrong test name
Huongg Mar 23, 2023
9b89c6c
sticky header for preview table
Huongg Mar 23, 2023
0455310
feature flag and connect the preview data with config table
Huongg Mar 23, 2023
3d54783
use more generic classname for metadata modal
Huongg Mar 23, 2023
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
1 change: 1 addition & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Please follow the established format:
- Remove metrics plots from metadata panel and add link to the plots on Experiment tracking. (#1268)
- Link plot and JSON dataset names from experiment tracking to the flowchart. (#1165)
- Bump minimum version of React from 16.8.6 to 17.0.2. (#1282)
- Show preview of data in metadata panel. (#1288)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth putting this at the top of the list since to users it's much more interesting than the other changes 😀 (maybe the react version point should go under bug fixes and other changes?)

Maybe also worth saying "preview of pandas.CSVDataSet and pandas.ExcelDataSet" too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hey i like the idea of including the "preview of pandas.CSVDataSet and pandas.ExcelDataSet" here too. Even though it will also mention it in our Release highlight in the UI, I guess no harm to mention here again.

I think the React version might be a major change actually but maybe @tynandebold can confirm this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The React version change will make this a major release, so probably ok to leave it where it is.


## Bug fixes and other changes

Expand Down
3 changes: 2 additions & 1 deletion package/kedro_viz/api/rest/responses.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""`kedro_viz.api.rest.responses` defines REST response types."""
# pylint: disable=missing-class-docstring,too-few-public-methods
# pylint: disable=missing-class-docstring,too-few-public-methods,invalid-name
import abc
from typing import Any, Dict, List, Optional, Union

Expand Down Expand Up @@ -116,6 +116,7 @@ class DataNodeMetadataAPIResponse(BaseAPIResponse):
image: Optional[str]
tracking_data: Optional[Dict]
run_command: Optional[str]
preview: Optional[Dict]

class Config:
schema_extra = {
Expand Down
17 changes: 17 additions & 0 deletions package/kedro_viz/models/flowchart.py
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,10 @@ def is_tracking_node(self):
"""Checks if the current node is a tracking data node"""
return self.is_json_node() or self.is_metric_node()

def is_preview_node(self):
"""Checks if the current node has a preview"""
return hasattr(self.kedro_obj, "_preview")


@dataclass
class TranscodedDataNode(GraphNode):
Expand Down Expand Up @@ -555,6 +559,8 @@ class DataNodeMetadata(GraphNodeMetadata):
# command to run the pipeline to this data node
run_command: Optional[str] = field(init=False, default=None)

preview: Optional[Dict] = field(init=False, default=None)

# TODO: improve this scheme.
def __post_init__(self, data_node: DataNode):
self.type = data_node.dataset_type
Expand All @@ -571,6 +577,7 @@ def __post_init__(self, data_node: DataNode):
data_node.is_plot_node()
or data_node.is_image_node()
or data_node.is_tracking_node()
or data_node.is_preview_node()
):
return

Expand All @@ -586,6 +593,16 @@ def __post_init__(self, data_node: DataNode):
self.image = dataset.load()
elif data_node.is_tracking_node():
self.tracking_data = dataset.load()
elif data_node.is_preview_node():
try:
self.preview = dataset._preview() # type: ignore
except Exception as exc: # pylint: disable=broad-except # pragma: no cover
logger.warning(
"'%s' could not be previewed. Full exception: %s: %s",
data_node.full_name,
type(exc).__name__,
exc,
)


@dataclass
Expand Down
2 changes: 1 addition & 1 deletion package/test_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ flake8~=5.0
fastapi[all]>=0.73.0, <0.93.0
isort~=5.11
matplotlib~=3.5
mypy~=1.0
mypy~=0.990
psutil==5.9.4 # same as Kedro for now
pylint~=2.16
pytest~=7.2
Expand Down
40 changes: 40 additions & 0 deletions package/tests/test_models/test_flowchart.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,38 @@ def test_data_node_metadata(self):
assert data_node_metadata.filepath == "/tmp/dataset.csv"
assert data_node_metadata.run_command == "kedro run --to-outputs=dataset"

def test_preview_data_node_metadata(self):
mock_preview_data = {
"columns": ["id", "company_rating", "company_location"],
"index": [0, 1, 2],
"data": [
[1, "90%", "London"],
[2, "80%", "Paris"],
[3, "40%", "Milan"],
],
}

preview_data_node = MagicMock()

preview_data_node.is_plot_node.return_value = False
preview_data_node.is_image_node.return_value = False
preview_data_node.is_tracking_node.return_value = False
preview_data_node.is_preview_node.return_value = True
preview_data_node.kedro_obj._preview.return_value = mock_preview_data
preview_node_metadata = DataNodeMetadata(data_node=preview_data_node)
assert preview_node_metadata.preview == mock_preview_data

def test_preview_data_node_metadata_not_exist(self):
preview_data_node = MagicMock()

preview_data_node.is_plot_node.return_value = False
preview_data_node.is_image_node.return_value = False
preview_data_node.is_tracking_node.return_value = False
preview_data_node.is_preview_node.return_value = True
preview_data_node.kedro_obj._preview.return_value = False
preview_node_metadata = DataNodeMetadata(data_node=preview_data_node)
assert preview_node_metadata.plot is None

def test_transcoded_data_node_metadata(self):
dataset = CSVDataSet(filepath="/tmp/dataset.csv")
transcoded_data_node = GraphNode.create_data_node(
Expand Down Expand Up @@ -420,6 +452,7 @@ def test_plotly_data_node_metadata(self):
plotly_data_node.is_plot_node.return_value = True
plotly_data_node.is_image_node.return_value = False
plotly_data_node.is_tracking_node.return_value = False
plotly_data_node.is_preview_node.return_value = False
plotly_data_node.kedro_obj.load.return_value = mock_plot_data
plotly_node_metadata = DataNodeMetadata(data_node=plotly_data_node)
assert plotly_node_metadata.plot == mock_plot_data
Expand All @@ -430,6 +463,7 @@ def test_plotly_data_node_dataset_not_exist(self):
plotly_data_node.is_image_node.return_value = False
plotly_data_node.is_tracking_node.return_value = False
plotly_data_node.kedro_obj.exists.return_value = False
plotly_data_node.is_preview_node.return_value = False
plotly_node_metadata = DataNodeMetadata(data_node=plotly_data_node)
assert plotly_node_metadata.plot is None

Expand All @@ -447,6 +481,7 @@ def test_plotly_json_dataset_node_metadata(self):
plotly_json_dataset_node.is_plot_node.return_value = True
plotly_json_dataset_node.is_image_node.return_value = False
plotly_json_dataset_node.is_tracking_node.return_value = False
plotly_json_dataset_node.is_preview_node.return_value = False
plotly_json_dataset_node.kedro_obj.load.return_value = mock_plot_data
plotly_node_metadata = DataNodeMetadata(data_node=plotly_json_dataset_node)
assert plotly_node_metadata.plot == mock_plot_data
Expand All @@ -461,6 +496,7 @@ def test_image_data_node_metadata(self):
image_dataset_node.is_image_node.return_value = True
image_dataset_node.is_plot_node.return_value = False
image_dataset_node.is_tracking_node.return_value = False
image_dataset_node.is_preview_node.return_value = False
image_dataset_node.kedro_obj.load.return_value = mock_image_data
image_node_metadata = DataNodeMetadata(data_node=image_dataset_node)
assert image_node_metadata.image == mock_image_data
Expand All @@ -470,6 +506,7 @@ def test_image_data_node_dataset_not_exist(self):
image_dataset_node.is_image_node.return_value = True
image_dataset_node.is_plot_node.return_value = False
image_dataset_node.kedro_obj.exists.return_value = False
image_dataset_node.is_preview_node.return_value = False
image_node_metadata = DataNodeMetadata(data_node=image_dataset_node)
assert image_node_metadata.image is None

Expand All @@ -485,6 +522,7 @@ def test_json_data_node_metadata(self):
json_data_node.is_image_node.return_value = False
json_data_node.is_tracking_node.return_value = True
json_data_node.is_metric_node.return_value = False
json_data_node.is_preview_node.return_value = False
json_data_node.kedro_obj.load.return_value = mock_json_data
json_node_metadata = DataNodeMetadata(data_node=json_data_node)
assert json_node_metadata.tracking_data == mock_json_data
Expand All @@ -495,6 +533,7 @@ def test_metrics_data_node_metadata_dataset_not_exist(self):
metrics_data_node.is_plot_node.return_value = False
metrics_data_node.is_image_node.return_value = False
metrics_data_node.is_metric_node.return_value = True
metrics_data_node.is_preview_node.return_value = False
metrics_data_node.kedro_obj.exists.return_value = False
metrics_node_metadata = DataNodeMetadata(data_node=metrics_data_node)
assert metrics_node_metadata.plot is None
Expand All @@ -505,6 +544,7 @@ def test_data_node_metadata_latest_tracking_data_not_exist(self):
plotly_data_node.is_image_node.return_value = False
plotly_data_node.is_tracking_node.return_value = False
plotly_data_node.kedro_obj.exists.return_value = False
plotly_data_node.kedro_obj.exists.return_value = False
plotly_node_metadata = DataNodeMetadata(data_node=plotly_data_node)
assert plotly_node_metadata.plot is None

Expand Down
61 changes: 40 additions & 21 deletions src/components/metadata-modal/metadata-modal.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import { connect } from 'react-redux';
import PlotlyChart from '../plotly-chart';
import PreviewTable from '../preview-table';
import CollapseIcon from '../icons/collapse';
import BackIcon from '../icons/back';
import NodeIcon from '../icons/node-icon';
Expand All @@ -12,8 +13,9 @@ import './metadata-modal.css';
const MetadataModal = ({ metadata, onToggle, visible }) => {
const hasPlot = Boolean(metadata?.plot);
const hasImage = Boolean(metadata?.image);
const hasPreview = Boolean(metadata?.preview);

if (!visible.metadataModal || (!hasPlot && !hasImage)) {
if (!visible.metadataModal || (!hasPlot && !hasImage && !hasPreview)) {
return null;
}

Expand All @@ -25,18 +27,28 @@ const MetadataModal = ({ metadata, onToggle, visible }) => {

return (
<div className="pipeline-metadata-modal">
<div className="pipeline-plot-modal__top">
<div className="pipeline-metadata-modal__top">
<button
className="pipeline-plot-modal__back"
className="pipeline-metadata-modal__back"
onClick={onCollapsePlotClick}
>
<BackIcon className="pipeline-plot-modal__back-icon"></BackIcon>
<span className="pipeline-plot-modal__back-text">Back</span>
<BackIcon className="pipeline-metadata-modal__back-icon"></BackIcon>
<span className="pipeline-metadata-modal__back-text">Back</span>
</button>
<div className="pipeline-plot-modal__header">
<NodeIcon className="pipeline-plot-modal__icon" icon={nodeTypeIcon} />
<span className="pipeline-plot-modal__title">{metadata.name}</span>
<div className="pipeline-metadata-modal__header">
<NodeIcon
className="pipeline-metadata-modal__icon"
icon={nodeTypeIcon}
/>
<span className="pipeline-metadata-modal__title">
{metadata.name}
</span>
</div>
{hasPreview && (
<div className="pipeline-metadata-modal__preview-text">
Previewing first 40 rows only
</div>
)}
</div>
{hasPlot && (
<PlotlyChart
Expand All @@ -56,19 +68,26 @@ const MetadataModal = ({ metadata, onToggle, visible }) => {
</div>
</div>
)}
<div className="pipeline-plot-modal__bottom">
<button
className="pipeline-plot-modal__collapse-plot"
onClick={onCollapsePlotClick}
>
<CollapseIcon className="pipeline-plot-modal__collapse-plot-icon"></CollapseIcon>
<span className="pipeline-plot-modal__collapse-plot-text">
{hasPlot
? 'Collapse Plotly Visualization'
: 'Collapse Matplotlib Image'}
</span>
</button>
</div>
{hasPreview && (
<div className="pipeline-metadata-modal__preview">
<PreviewTable data={metadata.preview} size="large" />
</div>
)}
{!hasPreview && (
<div className="pipeline-metadata-modal__bottom">
<button
className="pipeline-metadata-modal__collapse-plot"
onClick={onCollapsePlotClick}
>
<CollapseIcon className="pipeline-metadata-modal__collapse-plot-icon"></CollapseIcon>
<span className="pipeline-metadata-modal__collapse-plot-text">
{hasPlot
? 'Collapse Plotly Visualization'
: 'Collapse Matplotlib Image'}
</span>
</button>
</div>
)}
</div>
);
};
Expand Down
Loading