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

[Feat] Implement AgGrid model #289

Merged
merged 42 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
64a4eb9
Initial commit with rendering Grid
maxschulz-COL Jan 24, 2024
67b12e6
First implementation of separate model approach
maxschulz-COL Jan 25, 2024
61d190e
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 25, 2024
07ef9d0
Merge branch 'main' into feat/aggrid_separate_models
maxschulz-COL Jan 25, 2024
c186d91
Initial PR ready implementation
maxschulz-COL Feb 19, 2024
eeb2c68
Merge main
maxschulz-COL Feb 20, 2024
923be67
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 20, 2024
69a7b07
Linting
maxschulz-COL Feb 20, 2024
d81b1c8
Small updates to grid model
maxschulz-COL Feb 20, 2024
8703d0e
Remove ununsed files
maxschulz-COL Feb 20, 2024
e642da1
Change name from Grid to AGGrid
maxschulz-COL Feb 21, 2024
b92f0eb
First batch of PR comments
maxschulz-COL Feb 22, 2024
fed22b4
Merge branch 'main' into feat/aggrid_separate_models
maxschulz-COL Feb 22, 2024
251b4bc
Merge branch 'main' into feat/aggrid_separate_models
maxschulz-COL Feb 22, 2024
dab8c8e
Add changelog
maxschulz-COL Feb 22, 2024
dd7c3df
Turn off pagination
maxschulz-COL Feb 22, 2024
0872fe2
Merge main
maxschulz-COL Feb 22, 2024
842e68d
Replace datatable placeholder with aggrid
maxschulz-COL Feb 22, 2024
1ce60f6
PR comment on Errors
maxschulz-COL Feb 23, 2024
0c897b3
Merge branch 'main' into feat/aggrid_separate_models
maxschulz-COL Feb 23, 2024
574e0f1
Fix unit tests after model refactoring
maxschulz-COL Feb 23, 2024
c019521
Schema
maxschulz-COL Feb 23, 2024
7935a27
Merge branch 'main' into feat/aggrid_separate_models
maxschulz-COL Feb 23, 2024
1d40cf3
Update requirements
maxschulz-COL Feb 23, 2024
56e9f25
Fix tests and take over most tests from Table
maxschulz-COL Feb 23, 2024
3d9abc8
Add attribute tests for Graph, Table and AgGrid
maxschulz-COL Feb 23, 2024
9c1751f
Introduce tests for dash_ag_grid function
maxschulz-COL Feb 23, 2024
f427e27
Add test for filter interaction of ag_grid
maxschulz-COL Feb 23, 2024
a7e8e8c
Add tests for dash_data_table
maxschulz-COL Feb 23, 2024
33dcb40
Rename aggrid.py file
maxschulz-COL Feb 23, 2024
c3ffc76
Rename _dash_files files
maxschulz-COL Feb 23, 2024
11c9cf5
Refactor constant names
maxschulz-COL Feb 23, 2024
602d0fe
Delete unnecessary assert in attribute tests
maxschulz-COL Feb 23, 2024
634b1e8
Use assert_component_equal in _dash_* tests
maxschulz-COL Feb 23, 2024
cabdafa
Reshuffle tests to make the category clearer
maxschulz-COL Feb 23, 2024
fb6f206
Remaining PR comments
maxschulz-COL Feb 23, 2024
08f5bc6
Merge branch 'main' into feat/aggrid_separate_models
maxschulz-COL Feb 27, 2024
24e0e84
PR comments Petar
maxschulz-COL Feb 27, 2024
ec85e0c
Fix bug for missing ID when no actions but ID are defined
maxschulz-COL Feb 27, 2024
29d9aaf
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 27, 2024
f503f05
Fix pagination bug
maxschulz-COL Feb 27, 2024
f15ca91
Add tests to reflect bugfix
maxschulz-COL Feb 27, 2024
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<!--
maxschulz-COL marked this conversation as resolved.
Show resolved Hide resolved
A new scriv changelog fragment.

Uncomment the section that is right (remove the HTML comment wrapper).
-->

<!--
### Highlights ✨

- A bullet item for the Highlights ✨ category with a link to the relevant PR at the end of your entry, e.g. Enable feature XXX ([#1](https://github.com/mckinsey/vizro/pull/1))

-->
<!--
### Removed

- A bullet item for the Removed category with a link to the relevant PR at the end of your entry, e.g. Enable feature XXX ([#1](https://github.com/mckinsey/vizro/pull/1))

-->
<!--
### Added

- A bullet item for the Added category with a link to the relevant PR at the end of your entry, e.g. Enable feature XXX ([#1](https://github.com/mckinsey/vizro/pull/1))

-->
<!--
### Changed

- A bullet item for the Changed category with a link to the relevant PR at the end of your entry, e.g. Enable feature XXX ([#1](https://github.com/mckinsey/vizro/pull/1))

-->
<!--
### Deprecated

- A bullet item for the Deprecated category with a link to the relevant PR at the end of your entry, e.g. Enable feature XXX ([#1](https://github.com/mckinsey/vizro/pull/1))

-->
<!--
### Fixed

- A bullet item for the Fixed category with a link to the relevant PR at the end of your entry, e.g. Enable feature XXX ([#1](https://github.com/mckinsey/vizro/pull/1))

-->
<!--
### Security

- A bullet item for the Security category with a link to the relevant PR at the end of your entry, e.g. Enable feature XXX ([#1](https://github.com/mckinsey/vizro/pull/1))

-->
141 changes: 111 additions & 30 deletions vizro-core/examples/_dev/app.py
maxschulz-COL marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,51 +1,132 @@
"""Rough example used by developers."""
"""Example to show dashboard configuration."""

import dash_bootstrap_components as dbc
import numpy as np
import pandas as pd
import vizro.models as vm
import vizro.plotly.express as px
from vizro import Vizro
from vizro.actions import export_data, filter_interaction
from vizro.tables import dash_ag_grid, dash_data_table

iris = px.data.iris()

cards = vm.Page(
title="Cards",
components=[
vm.Card(
text="""
# This is an <h1> tag
## This is an <h2> tag
###### This is an <h6> tag
df = px.data.gapminder()
df_mean = (
df.groupby(by=["continent", "year"]).agg({"lifeExp": "mean", "pop": "mean", "gdpPercap": "mean"}).reset_index()
)

>
> Block quotes are used to highlight text.
>
df_transformed = df.copy()
df_transformed["lifeExp"] = df.groupby(by=["continent", "year"])["lifeExp"].transform("mean")
df_transformed["gdpPercap"] = df.groupby(by=["continent", "year"])["gdpPercap"].transform("mean")
df_transformed["pop"] = df.groupby(by=["continent", "year"])["pop"].transform("sum")
df_concat = pd.concat([df_transformed.assign(color="Continent Avg."), df.assign(color="Country")], ignore_index=True)

* Item 1
* Item 2

*This text will be italic*
grid_interaction = vm.Page(
title="AG Grid and Table Interaction",
components=[
vm.AgGrid(
id="table_country_new",
title="Click on a cell",
figure=dash_ag_grid(
id="dash_ag_grid_1",
data_frame=px.data.gapminder(),
),
actions=[vm.Action(function=filter_interaction(targets=["line_country"]))],
),
vm.Table(
id="table_country",
title="Click on a cell",
figure=dash_data_table(
id="dash_data_table_country",
data_frame=df,
columns=[{"id": col, "name": col} for col in df.columns],
sort_action="native",
style_cell={"textAlign": "left"},
),
actions=[vm.Action(function=filter_interaction(targets=["line_country"]))],
),
vm.Graph(
id="line_country",
figure=px.line(
df_concat,
title="Country vs. Continent",
x="year",
y="gdpPercap",
color="color",
labels={"year": "Year", "data": "Data", "gdpPercap": "GDP per capita"},
color_discrete_map={"Country": "#afe7f9", "Continent": "#003875"},
markers=True,
hover_name="country",
),
),
vm.Button(
text="Export data",
actions=[
vm.Action(
function=export_data(
targets=["line_country"],
)
),
],
),
],
controls=[
vm.Filter(column="continent", selector=vm.Dropdown(value="Europe", multi=False, title="Select continent")),
vm.Filter(column="year", selector=vm.RangeSlider(title="Select timeframe", step=1, marks=None)),
vm.Parameter(
targets=["line_country.y"],
selector=vm.Dropdown(
options=["lifeExp", "gdpPercap", "pop"], multi=False, value="gdpPercap", title="Choose y-axis"
),
),
],
)

_This will also be italic_

**This text will be bold**
df2 = px.data.stocks()
df2["date_as_datetime"] = pd.to_datetime(df2["date"])
df2["date_str"] = df2["date"].astype("str")
df2["perc_from_float"] = np.random.rand(len(df2))
df2["random"] = np.random.uniform(-100000.000, 100000.000, len(df2))

_You **can** combine them_
"""
)
grid_standard = vm.Page(
title="AG Grid Default",
components=[
vm.AgGrid(
figure=dash_ag_grid(
id="dash_ag_grid_2",
data_frame=df2,
),
),
],
)

graph = vm.Page(
title="Graph",
grid_custom = vm.Page(
title="AG Grid Custom",
components=[
vm.Graph(
id="scatter_relation",
figure=px.scatter(data_frame=px.data.gapminder(), x="gdpPercap", y="lifeExp", size="pop"),
vm.AgGrid(
figure=dash_ag_grid(
id="dash_ag_grid_3",
data_frame=df2,
columnDefs=[
{"field": "AAPL", "headerName": "Format Dollar", "cellDataType": "dollar"},
{"field": "AAPL", "headerName": "Format Euro", "cellDataType": "euro"},
{"field": "random", "headerName": "Format Numeric", "cellDataType": "numeric"},
{"field": "perc_from_float", "headerName": "Format Percent", "cellDataType": "percent"},
{
"field": "perc_from_float",
"headerName": "custom format",
"valueFormatter": {"function": "d3.format('.^30')(params.value)"},
},
],
),
),
],
)

dashboard = vm.Dashboard(pages=[cards, graph], navigation=vm.Navigation(nav_selector=vm.NavBar()))

dashboard = vm.Dashboard(
pages=[grid_interaction, grid_standard, grid_custom],
)

if __name__ == "__main__":
Vizro(external_stylesheets=[dbc.themes.BOOTSTRAP]).build(dashboard).run()
Vizro(assets_folder="../assets").build(dashboard).run()
1 change: 1 addition & 0 deletions vizro-core/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ classifiers = [
dependencies = [
"dash>=2.14.1", # 2.14.1 needed for compatibility with werkzeug
"dash_bootstrap_components",
"dash-ag-grid>=31.0.0",
"pandas",
"pydantic>=1.10.13", # must be synced with pre-commit mypy hook manually
"dash_mantine_components",
Expand Down
44 changes: 43 additions & 1 deletion vizro-core/schemas/0.1.12.dev0.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,40 @@
},
"additionalProperties": false
},
"AgGrid": {
"title": "AgGrid",
"description": "Wrapper for `dash-ag-grid.AgGrid` to visualize grids in dashboard.\n\nArgs:\n type (Literal[\"ag_grid\"]): Defaults to `\"ag_grid\"`.\n figure (CapturedCallable): AgGrid like object to be displayed. For more information see:\n [`dash-ag-grid.AgGrid`](https://dash.plotly.com/dash-ag-grid).\n title (str): Title of the table. Defaults to `\"\"`.\n actions (List[Action]): See [`Action`][vizro.models.Action]. Defaults to `[]`.",
"type": "object",
"properties": {
"id": {
"title": "Id",
"description": "ID to identify model. Must be unique throughout the whole dashboard.When no ID is chosen, ID will be automatically generated.",
"default": "",
"type": "string"
},
"type": {
"title": "Type",
"default": "ag_grid",
"enum": ["ag_grid"],
"type": "string"
},
"title": {
"title": "Title",
"description": "Title of the AgGrid",
"default": "",
"type": "string"
},
"actions": {
"title": "Actions",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/Action"
}
}
},
"additionalProperties": false
},
"Button": {
"title": "Button",
"description": "Component provided to `Page` to trigger any defined `action` in `Page`.\n\nArgs:\n type (Literal[\"button\"]): Defaults to `\"button\"`.\n text (str): Text to be displayed on button. Defaults to `\"Click me!\"`.\n actions (List[Action]): See [`Action`][vizro.models.Action]. Defaults to `[]`.",
Expand Down Expand Up @@ -168,7 +202,7 @@
},
"Table": {
"title": "Table",
"description": "Wrapper for table components to visualize in dashboard.\n\nArgs:\n type (Literal[\"table\"]): Defaults to `\"table\"`.\n figure (CapturedCallable): Table like object to be displayed. Current choices include:\n [`dash_table.DataTable`](https://dash.plotly.com/datatable).\n title (str): Title of the table. Defaults to `\"\"`.\n actions (List[Action]): See [`Action`][vizro.models.Action]. Defaults to `[]`.",
"description": "Wrapper for `dash_table.DataTable` to visualize tables in dashboard.\n\nArgs:\n type (Literal[\"table\"]): Defaults to `\"table\"`.\n figure (CapturedCallable): Table like object to be displayed. For more information see:\n [`dash_table.DataTable`](https://dash.plotly.com/datatable).\n title (str): Title of the table. Defaults to `\"\"`.\n actions (List[Action]): See [`Action`][vizro.models.Action]. Defaults to `[]`.",
"type": "object",
"properties": {
"id": {
Expand Down Expand Up @@ -306,6 +340,7 @@
"discriminator": {
"propertyName": "type",
"mapping": {
"ag_grid": "#/definitions/AgGrid",
"button": "#/definitions/Button",
"card": "#/definitions/Card",
"container": "#/definitions/Container",
Expand All @@ -315,6 +350,9 @@
}
},
"oneOf": [
{
"$ref": "#/definitions/AgGrid"
},
{
"$ref": "#/definitions/Button"
},
Expand Down Expand Up @@ -969,6 +1007,7 @@
"discriminator": {
"propertyName": "type",
"mapping": {
"ag_grid": "#/definitions/AgGrid",
"button": "#/definitions/Button",
"card": "#/definitions/Card",
"container": "#/definitions/Container",
Expand All @@ -978,6 +1017,9 @@
}
},
"oneOf": [
{
"$ref": "#/definitions/AgGrid"
},
{
"$ref": "#/definitions/Button"
},
Expand Down
1 change: 1 addition & 0 deletions vizro-core/snyk/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
dash>=2.14.1
dash_bootstrap_components
dash-ag-grid>=31.0.0
pandas
pydantic>=1.10.13
dash_mantine_components
Expand Down
62 changes: 6 additions & 56 deletions vizro-core/src/vizro/actions/_actions_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,31 +69,6 @@ def _apply_filters(data_frame: pd.DataFrame, ctds_filters: List[CallbackTriggerD
return data_frame


def _apply_graph_filter_interaction(
data_frame: pd.DataFrame, target: str, ctd_filter_interaction: Dict[str, CallbackTriggerDict]
) -> pd.DataFrame:
ctd_click_data = ctd_filter_interaction["clickData"]
if not ctd_click_data["value"]:
return data_frame

source_graph_id: ModelID = ctd_click_data["id"]
source_graph_actions = _get_component_actions(model_manager[source_graph_id])
try:
custom_data_columns = model_manager[source_graph_id]["custom_data"]
except KeyError as exc:
raise KeyError(f"No `custom_data` argument found for source graph with id {source_graph_id}.") from exc

customdata = ctd_click_data["value"]["points"][0]["customdata"]

for action in source_graph_actions:
if action.function._function.__name__ != "filter_interaction" or target not in action.function["targets"]:
continue
for custom_data_idx, column in enumerate(custom_data_columns):
data_frame = data_frame[data_frame[column].isin([customdata[custom_data_idx]])]

return data_frame


def _get_parent_vizro_model(_underlying_callable_object_id: str) -> VizroBaseModel:
from vizro.models import VizroBaseModel

Expand All @@ -108,41 +83,16 @@ def _get_parent_vizro_model(_underlying_callable_object_id: str) -> VizroBaseMod
)


def _apply_table_filter_interaction(
data_frame: pd.DataFrame, target: str, ctd_filter_interaction: Dict[str, CallbackTriggerDict]
) -> pd.DataFrame:
ctd_active_cell = ctd_filter_interaction["active_cell"]
ctd_derived_viewport_data = ctd_filter_interaction["derived_viewport_data"]
if not ctd_active_cell["value"] or not ctd_derived_viewport_data["value"]:
return data_frame

# ctd_active_cell["id"] represents the underlying table id, so we need to fetch its parent Vizro Table actions.
source_table_actions = _get_component_actions(_get_parent_vizro_model(ctd_active_cell["id"]))

for action in source_table_actions:
if action.function._function.__name__ != "filter_interaction" or target not in action.function["targets"]:
continue
column = ctd_active_cell["value"]["column_id"]
derived_viewport_data_row = ctd_active_cell["value"]["row"]
clicked_data = ctd_derived_viewport_data["value"][derived_viewport_data_row][column]
data_frame = data_frame[data_frame[column].isin([clicked_data])]

return data_frame


def _apply_filter_interaction(
data_frame: pd.DataFrame, ctds_filter_interaction: List[Dict[str, CallbackTriggerDict]], target: str
) -> pd.DataFrame:
for ctd_filter_interaction in ctds_filter_interaction:
if "clickData" in ctd_filter_interaction:
data_frame = _apply_graph_filter_interaction(
data_frame=data_frame, target=target, ctd_filter_interaction=ctd_filter_interaction
)

if "active_cell" in ctd_filter_interaction and "derived_viewport_data" in ctd_filter_interaction:
data_frame = _apply_table_filter_interaction(
data_frame=data_frame, target=target, ctd_filter_interaction=ctd_filter_interaction
)
triggered_model = model_manager[ctd_filter_interaction["modelID"]["id"]]
data_frame = triggered_model._filter_interaction(
data_frame=data_frame,
target=target,
ctd_filter_interaction=ctd_filter_interaction,
)

return data_frame

Expand Down
Loading
Loading