Feat: put table's callback and layout in factory

This commit is contained in:
Bertrand Benjamin 2024-07-28 18:49:07 +02:00
parent 612df0a8eb
commit 7fb7bc6f5c
2 changed files with 121 additions and 117 deletions

View File

@ -1,6 +1,7 @@
import dash import dash
from dash import Dash, html, dcc from dash import Dash, html, dcc
from .pages import home, config, stage, schema, table from .pages import home, config, stage, schema, table
from .datalake import stages
external_scripts = [ external_scripts = [
{'src': 'https://cdn.tailwindcss.com'} {'src': 'https://cdn.tailwindcss.com'}
@ -36,8 +37,9 @@ dash.register_page(
dash.register_page( dash.register_page(
table.__name__, table.__name__,
path_template='/stg/<stage_name>/schm/<schema_name>/table/<table_name>', path_template='/stg/<stage_name>/schm/<schema_name>/table/<table_name>',
layout=table.layout layout=table.layout_factory(stages)
) )
table.callback_factory(app)
app.layout = html.Div([ app.layout = html.Div([
html.Div([ html.Div([

View File

@ -1,126 +1,128 @@
from dash import html, dcc, dash_table, callback, Input, Output, State from dash import html, dcc, dash_table, Input, Output, State
from dash.exceptions import PreventUpdate from dash.exceptions import PreventUpdate
from ..datalake import stages
from ..libs.stage.stage import AbstractStage from ..libs.stage.stage import AbstractStage
def layout_factory(stages: list[AbstractStage]):
def layout(stage_name=None, schema_name=None, table_name=None):
stage = stages[stage_name]
df = stage.read(table=table_name, schema=schema_name)
return html.Div([
dcc.Store(id="table_backup"),
html.Div([
html.H2([
dcc.Link(
f"{stage.name}",
href=f"/stage/{stage.name}",
className="hover:underline"
),
html.Span(" > "),
dcc.Link(
f"{schema_name}",
href=f"/stg/{stage.name}/schema/{schema_name}",
className="hover:underline"
),
html.Span(" > "),
html.Span(table_name),
],
className="text-2xl"
),
html.Div([
html.Button(
"Editer",
id="btn_edit",
className="rounded border px-2 py-1",
style={"display": "block"}
),
html.Button(
"Sauver",
id="btn_save",
className="rounded border px-2 py-1 border-green-500 hover:bg-green-500",
style={"display": "none"}
),
html.Button(
"Annuler",
id="btn_cancel",
className="rounded border px-2 py-1 border-red-500 hover:bg-red-500",
style={"display": "none"}
),
],
className="flex flex-row space-x-2",
id="toolbar"
),
],
className="flex flex-row justify-between p-4"
),
html.Div([
html.Div([
dash_table.DataTable(
id="datatable",
data=df.to_dict('records'),
columns=[{"name": i, "id": i} for i in df.columns],
filter_action="native",
sort_action="native",
sort_mode="multi",
editable=False
)
])
],
className="overflow-y-auto"
),
],
className="p-2"
)
return layout
def layout(stage_name=None, schema_name=None, table_name=None):
stage = stages[stage_name] def callback_factory(app):
df = stage.read(table=table_name, schema=schema_name) @app.callback(
return html.Div([ Output("datatable", 'editable', allow_duplicate=True),
dcc.Store(id="table_backup"), Output("table_backup", 'data'),
html.Div([ Input("btn_edit", "n_clicks"),
html.H2([ State("datatable", 'data'),
dcc.Link( prevent_initial_call=True
f"{stage.name}",
href=f"/stage/{stage.name}",
className="hover:underline"
),
html.Span(" > "),
dcc.Link(
f"{schema_name}",
href=f"/stg/{stage.name}/schema/{schema_name}",
className="hover:underline"
),
html.Span(" > "),
html.Span(table_name),
],
className="text-2xl"
),
html.Div([
html.Button(
"Editer",
id="btn_edit",
className="rounded border px-2 py-1",
style={"display": "block"}
),
html.Button(
"Sauver",
id="btn_save",
className="rounded border px-2 py-1 border-green-500 hover:bg-green-500",
style={"display": "none"}
),
html.Button(
"Annuler",
id="btn_cancel",
className="rounded border px-2 py-1 border-red-500 hover:bg-red-500",
style={"display": "none"}
),
],
className="flex flex-row space-x-2",
id="toolbar"
),
],
className="flex flex-row justify-between p-4"
),
html.Div([
html.Div([
dash_table.DataTable(
id="datatable",
data=df.to_dict('records'),
columns=[{"name": i, "id": i} for i in df.columns],
filter_action="native",
sort_action="native",
sort_mode="multi",
editable=False
)
])
],
className="overflow-y-auto"
),
],
className="p-2"
) )
def activate_editable(n_clicks, df_src):
if n_clicks is None:
@callback( raise PreventUpdate
Output("datatable", 'editable', allow_duplicate=True), if n_clicks > 0:
Output("table_backup", 'data'), df_backup = df_src.copy()
Input("btn_edit", "n_clicks"), return True, df_backup
State("datatable", 'data'),
prevent_initial_call=True
)
def activate_editable(n_clicks, df_src):
if n_clicks is None:
raise PreventUpdate raise PreventUpdate
if n_clicks > 0:
df_backup = df_src.copy()
return True, df_backup
raise PreventUpdate
@callback( @app.callback(
Output("datatable", 'editable'), Output("datatable", 'editable', allow_duplicate=True),
Output("datatable", 'data'), Output("datatable", 'data', allow_duplicate=True),
Input("btn_cancel", "n_clicks"), Input("btn_cancel", "n_clicks"),
State("table_backup", 'data'), State("table_backup", 'data'),
prevent_initial_call=True prevent_initial_call=True
) )
def cancel_modifications(n_clicks, data): def cancel_modifications(n_clicks, data):
if n_clicks is None: if n_clicks is None:
raise PreventUpdate
if n_clicks > 0 and data is not None:
return False, data.copy()
raise PreventUpdate raise PreventUpdate
if n_clicks > 0 and data is not None:
return False, data.copy()
raise PreventUpdate
# @callback( @app.callback(
# Output("datatable", 'editable'), Output("datatable", 'editable'),
# Input("btn_save", "n_clicks"), Output("datatable", 'data'),
# State("datatable", 'editable'), Input("btn_save", "n_clicks"),
# ) State("datatable", 'editable'),
# def save_modifications(n_clicks, editable): )
# if n_clicks is None: def save_modifications(n_clicks, editable):
# raise PreventUpdate if n_clicks is None:
# if n_clicks > 0: raise PreventUpdate
# return not editable if n_clicks > 0:
# return editable return not editable
return editable
@callback( @app.callback(
Output("btn_edit", "style"), Output("btn_edit", "style"),
Output("btn_save", "style"), Output("btn_save", "style"),
Output("btn_cancel", "style"), Output("btn_cancel", "style"),
Input("datatable", "editable"), Input("datatable", "editable"),
) )
def toolbar(editable): def toolbar(editable):
if editable: if editable:
return {"display": "none"}, {"display": "block"}, {"display": "block"} return {"display": "none"}, {"display": "block"}, {"display": "block"}
return {"display": "block"}, {"display": "none"}, {"display": "none"} return {"display": "block"}, {"display": "none"}, {"display": "none"}