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

Fix all Dash deprecation errors as well as wrong schema.json example #13

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,6 @@ dmypy.json

# Pyre type checker
.pyre/

.DS_Store
.idea
2 changes: 1 addition & 1 deletion examples/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
"description": "subforms children array w/ size defined by min/max items and referenced by definitions",
"minItems": 2,
"maxItems": 2,
"items": [{"$ref": "#/definitions/ChildrenExample"}]
"items": {"$ref": "#/definitions/ChildrenExample"}
}
}
}
Expand Down
8 changes: 3 additions & 5 deletions examples/standalone_example.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import dash
import dash_bootstrap_components as dbc
import dash_html_components as html
import json
from pathlib import Path
from json_schema_to_dash_forms import SchemaFormContainer
from dash.dependencies import Input, Output, State
Expand Down Expand Up @@ -60,7 +58,7 @@
dbc.Textarea(
id='display_results',
className='string_input',
bs_size="lg",
size="lg",
readOnly=True,
style={'font-size': '16px', 'min-height': '250px', 'max-height': '500px'}
),
Expand Down Expand Up @@ -105,10 +103,10 @@ def show_data_from_internal_dict(trigger, is_open):
if not trigger:
return dash.no_update

alerts, output = my_form.data_to_nested() # Read internal dict and get missing required fields and output nested dict
alerts, output = my_form.data_to_nested() # Read internal dict and get missing required fields and output nested dict

if alerts is not None:
return True, alerts, '' # If any missing fields return alerts
return True, alerts, '' # If any missing fields return alerts

# Else show json data
json_string = json.dumps(output, indent=4)
Expand Down
55 changes: 31 additions & 24 deletions json_schema_to_dash_forms/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

import dash
import dash_bootstrap_components as dbc
import dash_core_components as dcc
import dash_html_components as html
from dash import dcc
from dash import html
import numpy as np
from dash.dependencies import Input, Output, State, ALL, MATCH
from dash_cool_components import TagInput, DateTimePicker
Expand All @@ -14,7 +14,7 @@
from .utils import make_filebrowser_modal


class SchemaFormItem(dbc.FormGroup):
class SchemaFormItem(dbc.Form):
def __init__(self, label, value, input_id, parent, required=False):
super().__init__([])

Expand Down Expand Up @@ -107,7 +107,7 @@ def get_field_input(self, value, input_id, description=None, required=False):
field_input = dbc.Textarea(
id=compound_id,
className='string_input',
bs_size="lg",
size="lg",
style={'font-size': '16px'}
)

Expand Down Expand Up @@ -145,7 +145,7 @@ def get_field_input(self, value, input_id, description=None, required=False):
field_input = html.Div([
dbc.InputGroup([
input_path,
dbc.InputGroupAddon(btn_open_filebrowser, addon_type="append"),
btn_open_filebrowser,
]),
modal
])
Expand All @@ -155,7 +155,7 @@ def get_field_input(self, value, input_id, description=None, required=False):
default = value.get('default', False)
field_input = dbc.Checkbox(
id=compound_id,
checked=default
value=default
)

else:
Expand Down Expand Up @@ -268,7 +268,8 @@ def __init__(self, schema, key, container=None, parent_form=None):

header_text = schema.get('title', self.id.split('-')[-1])
self.header = dbc.CardHeader(
[dbc.Button(html.H4(header_text, style={"color": 'black'}, className="title_" + key), color='link', id={"type": "collapsible-toggle", "container": f"{self.container.id}", 'index': f'{self.id}-collapsible'})],
[dbc.Button(html.H4(header_text, style={"color": 'black'}, className="title_" + key), color='link',
id={"type": "collapsible-toggle", "container": f"{self.container.id}", 'index': f'{self.id}-collapsible'})],
style={'padding': '10px'}
)
self.body = dbc.CardBody([])
Expand All @@ -281,10 +282,11 @@ def __init__(self, schema, key, container=None, parent_form=None):
if 'properties' in schema:
self.make_form(properties=schema['properties'])

self.body = dbc.Collapse(self.body, id={"type": 'collapsible-body', "container": f"{self.container.id}" ,"index": f'{self.id}-collapsible'}, is_open=True)
self.body = dbc.Collapse(self.body,
id={"type": 'collapsible-body', "container": f"{self.container.id}", "index": f'{self.id}-collapsible'},
is_open=True)
self.children = [self.header, self.body]


def make_form(self, properties):
"""Iterates over properties of schema and assembles form items"""
for k, v in properties.items():
Expand Down Expand Up @@ -314,7 +316,7 @@ def make_form(self, properties):
continue

# If item is an array of subforms, it should have 'minItems'
if 'minItems' in v:
if 'minItems' in v:
value = []
if '$ref' in v['items']: # search for reference somewhere else in the root schema
for i in v['items']['$ref'].split('/'):
Expand All @@ -325,7 +327,7 @@ def make_form(self, properties):
schema = aux
else:
schema = v['items']

# Creates 'minItems' number of subforms
for index in range(v['minItems']):
iform = SchemaForm(schema=schema, key=f'{k}-{index}', parent_form=self)
Expand Down Expand Up @@ -388,8 +390,10 @@ def __init__(self, id, schema, parent_app, root_path=None):

# Hidden components that serve to trigger callbacks
self.children_triggers = [
html.Div(id={'type': 'external-trigger-update-forms-values', 'index': id + '-external-trigger-update-forms-values'}, style={'display': 'none'}),
html.Div(id={'type': 'external-trigger-update-links-values', 'index': f'{id}-external-trigger-update-links-values'}, style={"display": "none"}),
html.Div(id={'type': 'external-trigger-update-forms-values', 'index': id + '-external-trigger-update-forms-values'},
style={'display': 'none'}),
html.Div(id={'type': 'external-trigger-update-links-values', 'index': f'{id}-external-trigger-update-links-values'},
style={"display": "none"}),
html.Div(id=f'{id}-external-trigger-update-internal-dict', style={'display': 'none'}),
html.Div(id=f'{id}-output-update-finished-verification', style={'display': 'none'}),
html.Div(id=id + '-trigger-update-links-values', style={'display': 'none'}),
Expand All @@ -411,7 +415,7 @@ def __init__(self, id, schema, parent_app, root_path=None):

self.update_forms_values_callback_outputs = [
Output(dict(_args_dict, data_type='path'), 'value'),
Output(dict(_args_dict, data_type='boolean'), 'checked'),
Output(dict(_args_dict, data_type='boolean'), 'value'),
Output(dict(_args_dict, data_type='string'), 'value'),
Output(dict(_args_dict, data_type='datetime'), 'defaultValue'),
Output(dict(_args_dict, data_type='tags'), 'injectedTags'),
Expand All @@ -421,7 +425,7 @@ def __init__(self, id, schema, parent_app, root_path=None):
]
self.update_forms_values_callback_states = [
State(dict(_args_dict, data_type='path'), 'value'),
State(dict(_args_dict, data_type='boolean'), 'checked'),
State(dict(_args_dict, data_type='boolean'), 'value'),
State(dict(_args_dict, data_type='string'), 'value'),
State(dict(_args_dict, data_type='datetime'), 'value'),
State(dict(_args_dict, data_type='tags'), 'value'),
Expand All @@ -436,7 +440,7 @@ def __init__(self, id, schema, parent_app, root_path=None):
//const element = document.getElementById(JSON.stringify(ids, Object.keys(ids).sort()))

ctx = dash_clientside.callback_context

if (typeof ctx.triggered[0] === "undefined"){
return dash_clientside.no_update
}
Expand All @@ -447,17 +451,17 @@ def __init__(self, id, schema, parent_app, root_path=None):

}
""",
Output({"type":'collapsible-body', "container": f'{self.id}', "index": MATCH}, 'is_open'),
Output({"type": 'collapsible-body', "container": f'{self.id}', "index": MATCH}, 'is_open'),
[Input({"type": 'collapsible-toggle', "container": f'{self.id}', "index": MATCH}, 'n_clicks')],
[State({"type":'collapsible-body', "container": f'{self.id}', "index": MATCH}, 'is_open')]
[State({"type": 'collapsible-body', "container": f'{self.id}', "index": MATCH}, 'is_open')]
)

@self.parent_app.callback(
Output(f'{self.id}-output-update-finished-verification', 'children'),
[Input(f'{self.id}-external-trigger-update-internal-dict', 'children')],
[
State({'type': 'metadata-input', 'container_id': self.id, 'data_type': 'path', 'index': ALL}, 'value'),
State({'type': 'metadata-input', 'container_id': self.id, 'data_type': 'boolean', 'index': ALL}, 'checked'),
State({'type': 'metadata-input', 'container_id': self.id, 'data_type': 'boolean', 'index': ALL}, 'value'),
State({'type': 'metadata-input', 'container_id': self.id, 'data_type': 'string', 'index': ALL}, 'value'),
State({'type': 'metadata-input', 'container_id': self.id, 'data_type': 'datetime', 'index': ALL}, 'value'),
State({'type': 'metadata-input', 'container_id': self.id, 'data_type': 'tags', 'index': ALL}, 'value'),
Expand Down Expand Up @@ -531,7 +535,8 @@ def update_forms_values(trigger, trigger_all, *states):
if context['type'] == 'external-trigger-update-forms-values' and all((trg is None) or trg == [] for trg in trigger):
raise dash.exceptions.PreventUpdate

if context['type'] == 'internal-trigger-update-forms-values' and all((trg is None) or trg == [] or trg == '' for trg in trigger_all):
if context['type'] == 'internal-trigger-update-forms-values' and all(
(trg is None) or trg == [] or trg == '' for trg in trigger_all):
raise dash.exceptions.PreventUpdate

output_path = []
Expand Down Expand Up @@ -587,7 +592,8 @@ def update_forms_links(trigger, trigger_all, name_change):
if 'type' in trigger_source:
trigger_source = json.loads(trigger_source)['type']

if trigger_source == 'external-trigger-update-links-values' and all((trg is None) or trg == [] or trg == '' for trg in trigger_all):
if trigger_source == 'external-trigger-update-links-values' and all(
(trg is None) or trg == [] or trg == '' for trg in trigger_all):
raise dash.exceptions.PreventUpdate
if trigger_source == f'{self.id}-trigger-update-links-values' and trigger is None:
raise dash.exceptions.PreventUpdate
Expand Down Expand Up @@ -640,7 +646,7 @@ def update_data(self, data, key=None):
"""Update data in the internal mapping dictionary of this Container"""
if key is None:
key = self.id

# Update dict with incoming data
for k, v in data.items():
if k in self.skiped_forms:
Expand Down Expand Up @@ -700,7 +706,8 @@ def data_to_nested(self):

for k, v in self.data.items():
field_value = v['value']
if v['required'] and (field_value is None or (isinstance(field_value, str) and field_value.isspace()) or field_value == '' or (str(field_value) == str(self.root_path))):
if v['required'] and (field_value is None or (isinstance(field_value, str) and field_value.isspace()) or field_value == '' or (
str(field_value) == str(self.root_path))):
empty_required_fields.append(k)
alert_children.append(html.A(
k,
Expand All @@ -719,7 +726,7 @@ def data_to_nested(self):
elif element != master_key_name:
curr_dict = {element: curr_dict}
else:
#if element == master_key_name:
# if element == master_key_name:
dicts_list.append(curr_dict)

for e in dicts_list:
Expand Down
12 changes: 3 additions & 9 deletions json_schema_to_dash_forms/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import dash
import dash_bootstrap_components as dbc
import dash_html_components as html
from dash import html
from dash.dependencies import Input, Output, State
from dash_cool_components import KeyedFileBrowser
from pathlib import Path
Expand Down Expand Up @@ -56,15 +56,9 @@ def __init__(self, parent_app, id_suffix, root_dir=None, display=None):

# Button part
input_group = dbc.InputGroup([
dbc.InputGroupAddon(
dbc.Button(button_text, color='dark', id="button_file_browser_" + id_suffix),
addon_type="prepend",
),
dbc.Button(button_text, color='dark', id="button_file_browser_" + id_suffix),
dbc.Input(id="chosen-filebrowser-" + id_suffix, placeholder=""),
dbc.InputGroupAddon(
dbc.Button('Submit', color='dark', id='submit-filebrowser-' + id_suffix),
addon_type='prepend',
),
dbc.Button('Submit', color='dark', id='submit-filebrowser-' + id_suffix)
])

# Collapsible part - file browser
Expand Down