GithubHelp home page GithubHelp logo

dash-labs's Introduction

Dash Labs tech preview

This repository contains a work-in-progress technical preview of potential future Dash features.

🚧 Dash Labs features are not guaranteed to land in the official dash package. These features are also not officially supported by Plotly's Support Team or by Dash Enterprise. We recommend waiting for these features to land in dash before using them in a Production Environment. 🚧

Documentation

The documentation for Dash Labs can be found in the docs/ directory.

Archived in dash-labs v0.4.0:

Multi-Page App Docs

New in dash-labs>=1.0.0:

Multi-Page App Demos

Examples and demos are located in the docs/demos directory.

  • multi_page_basics
    • Minimal examples of all the features and basic quickstart apps. (see chapter 8 for details.)
  • multi_page_example1
    • A quickstart app using dash-bootstrap-components and some simple callbacks.
  • multi_page_layout_functions
    • An app that creates a sidebar menu for certain pages. (See chapter 11 for details.)
  • multi_page_long_callback
    • An example of how to use @app.long_callback() with pages/
  • multi_page_meta_tags
    • The example app used to show how the meta tags are generated. (See chapter 10 for details.)
  • multi_page_nested_folders
    • This is the example app used in chapter 9.
  • multi_page_query_strings
    • An example of using query strings in the URL to pass parameters from one page to another.

Installation

To install the latest version of dash-labs:

$ pip install -U dash-labs

To install the archived version:

$ pip install dash-labs==0.4.0

We also recommend installing Pandas, which is required by Plotly Express and used in many of our examples.

dash-labs's People

Contributors

annmariew avatar bradley-erickson avatar charmingdata avatar chriddyp avatar gvwilson avatar johnkangw avatar jonmmease avatar neumann-nico avatar nicolaskruchten avatar t4rk1n avatar tcbegley avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dash-labs's Issues

Figure Templates

I'm a big fan of Dash Labs and one of my favorite features is the way it generates the figure templates. It inspired me to create a new library of Bootstrap themed figure templates that can be used with any version of Dash. See the forum post here

When I was working on this project, I noticed that the Dash Labs figure templates updates the colorways, but not the colorscales. Those remain the Plasma default regardless of the Bootstrap theme selected. Also the geo figures have a black background based on the plotly_dark figure template.

You can see it using this demo app:

This is the current dash labs figure template for the Slate theme

image

This is an updated template from the dash-bootstrap-templates library See more here

image

Would you like a pull request to update the dash-labs templates for the geo figures and the colorscales, or do you prefer to keep this type of change in a third party library?

Issue with 'new_dropdown'

I am currently on a web-app which involves using the new_dropdown element however I am having a hard time figuring out how enable multi selection. I am used to being able to just call 'multi = True', however, when i try that i get the following "TypeError: new_dropdown() got an unexpected keyword argument 'multi'", please let me know if there is another way to enable multi selection. Thanks!

DX as a plugin?

Assuming we can make app.callback fully backward compatible after adding the new functionality, what about structuring dx as a plugin so it can be used with app.callback from the beginning?

So instead of:

app = dash.Dash(__name__)

@dx.callback(
    app,
    ...
)

You'd have:

app = dash.Dash(__name__, plugins=[dx])

@app.callback(
    ...
)

Then the transition to Dash 2.0 would just be to remove the plugin, the decorator would be unchanged.

request: have a new job replace an old job

These new dash features look great, and are similar so something we need for our project. Essentially, we are running plots with multiple controllable parameters where a single parameter tweak might take a long time to render. If a user makes one tweak and then another, they will have to wait for the first plot to render and then wait for the second plot. We would like to change this so the second tweak cancels the current job and only runs the new job. This looks very similar to your cancel button, but it would be ideal if a number of widgets could be part of a group where they all cancel each other because they are all parameters of the same plot.

table falling outside of template container

Some context: I have been creating an app that pulls data from google sheets and displaying the results as output. However when I do so, the data table goes outside of the the container.

Here is the data table code (basically taken from the example code in the docs). Any suggestions?
Screen Shot 2021-05-10 at 4 48 17 PM

table_plugin = dl.component_plugins.DataTablePlugin(
    df = configuration.config.get_client_data(),
    page_size = 10,
    template = tpl,
    sort_mode ='single',
    filterable=True,
    serverside=False,
)
table_plugin.install_callback(app)

tpl.add_component(dropdown,label="Choose a sheet",role="input")
tpl.add_component(dropdown_worksheets,label="Choose a worksheet",role="input")


app.layout = tpl.layout(app)
if __name__ == "__main__":
    app.run_server(debug=True)

dcc.Store Support

Do predefined templates in dash_labs support using the dcc.Store component
(https://dash.plotly.com/dash-core-components/store)?
If so, what might be the best way to use them in conjunction with a predefined template? When I attempt to have them as an output:

app = dash.Dash(__name__, plugins=[dl.plugins.FlexibleCallbacks()])
tpl = dl.templates.DbcRow(
    app, title="Select Correct Contours")

store = dcc.Store(id='store')
tpl.add_component(store, location='left')
@app.callback(
    args=dict(good=tpl.new_button("Good", label=None, id='GOOD'),
              bad=tpl.new_button('Bad', label=None, id='BAD')),
    output=[tpl.new_div(), tpl.new_div(), store],
    template=tpl)
def callback(good, bad, store):
     pass

app.layout = dbc.Container(fluid=True, children=tpl.children)
if __name__ == "__main__":
    app.run_server(debug=True)

I get this error:

Traceback (most recent call last):
  File "E:\kjans\Anaconda\coordinates\AI_Project\select_dashboard\main.py", line 59, in <module>
    @app.callback(
  File "C:\Users\KyleJanson\.conda\envs\py39\lib\site-packages\dash_labs\_callback.py", line 278, in _callback
    all_outputs, output_form = _normalize_output(output, template)
  File "C:\Users\KyleJanson\.conda\envs\py39\lib\site-packages\dash_labs\_callback.py", line 152, in _normalize_output
    raise ValueError("Invalid dependency: {}".format(dep))
ValueError: Invalid dependency: Store(id='store')

I've tried a few different approaches, but so far, the above error has been consistent across all of them.

`from dash_labs import dash2 as d2` ?

In the interests of continuing to use the dash_labs concept after Dash 2 is out, I would suggest moving from import dash_labs as dl to something like from dash_labs import dash2 as d2, so that later on we can add other experimental stuff to the dash_labs module and from dash_labs import new_thing etc etc.

I've made this suggestion a couple of times but unsure if folks don't like it or just forget about it, so I'll log it here and if it's the former let's just say so, close this issue and I'll let up ;)

Project Status

Multi Page Apps

A better, easier way to make Multi-page apps with Dash

Status

🎉 Now available in dash 2.5.1!

Convert your multi-page app from a dash-labs pages plug-in to the pages feature in dash 2.5.1 in 3 easy steps:

  1. Remove import dash_labs as dl or upgrade dash-labs to V1.1.0
    There is a conflict between dash-labs versions less than 1.1.0 when running a pages app in dash 2.5.1

  2. Change:

app = Dash(__name__, plugins=[dl.plugins.pages])

to:

app = Dash(__name__, use_pages=True)
  1. Change:
dl.plugins.page_container

to:

dash.page_container

That's it!

👉 Thepages feature will no longer be developed and maintained here in dash-labs. I recommend all dash-labs multi-page apps be converted to use the pages feature in dash 2.5.1



MarkdownAIO

MarkdownAIO is a Dash feature that allows you to write Dash Apps as Markdown files. Simply pass in a Markdown file and MarkdownAIO will return a set of components with the option to display and/or execute code blocks. So it’s part:

  • Documentation helper
  • Markdown / Text authoring tool
  • Easy way to bring a markdown file into an app without doing open('file.md')

Status: Pull request in Dash Labs. Feature preview available. Project on hold pending re-write to make it easier to write secure dash apps

Summary of Open items:

  • Current way of running the code with exec can increase the risk of running malicious code. Project is on hold until this issue is solved. One potential solution is to only run code added to a markdown file like this: {% include example1.py %} This will ensure that only code on the server can be executed.

  • Raise error if using MarkdownAIO with exec within a callback. See discussion here: #82 (comment)

  • Improve the Stylesheet and add examples. Or find a better way to do this.

  • Do a dash-labs release

  • Write a parser for the yaml front-matter in Markdown files.
    The purpose is to improve security by limiting the scope to pages/ and MarkdownAIO data. Use json.loads instead of eval or exec. This will also remove the pyaml and python-frontmatter dependency. See discussion: #82 (comment)

  • Improve error handling and error messages

  • Write community forum announcement

  • Create dash pull request 🎊

  • Documentation

    • online feature preview: https://dashlabs.pythonanywhere.com/markdownaio/overview
    • Review, update and add content.
    • Add more info and concrete examples about writing secure dash apps with MarkdownAIO
    • Add examples for front-matter in Markdown files.
    • how to escape three back tick code fences in the docs
    • how to escape the {% include myfile.py %} in docs
  • Feature Request - Add Markdown files to hot reload in dash.
    That way users can have the same hot-reloading dev experience when working in markdown. Pull request in Dash.

  • Feature request- Be able to use different sub-components.
    Use-case is to use Prism from the dash-mantine-componets library to display code.

  • Feature request - Be able to change defaults globally so it doesn't have to be done for each MarkdownAIO() instance.

Multi-Page Apps: Location of pages directory

It seems that plugin.pages expects current working to contain pages, so they can be imported like. from pages.my_page import layout.
However the company I work for require the structure of the project to be like: from <src-directory>.pages.my_page import layout.
Is there any way to accommodate this currently or would it need a PR?

Multi Page Apps: Documentation

Here is the documentation for the Multi Page Apps:

We welcome all pull requests to help improve the documentation, even as minor as fixing a typo or writing a better sentence. If you would like to contribute to Dash, but are not sure how, this is a great place to start.

Our goal is to create high quality documentation that can be added directly to the official Dash documentation when new features are added to Dash. You can help -- even if you're new to Dash. Try following the instructions to make a multi-page app. Did we miss any steps? Is anything unclear? If so, please let us know

Document how to use DataTablePlugin without a template

It's mostly possible to use the DataTablePlugin without constructing a template, but right now the template is used to select the appropriate DataTable component (either from dashtable or ddk).

If we add a ddk=False argument, then it would be possible to use DataTablePlugin with DDK without constructing a ddk template.

cc @chriddyp

Multi-Page Apps - `order` doesn't work

It appears that adding order to dash.register_pagedoesn't work. It populatessupplied_order, in dash_page_registry but does not affect the actual order of the pages and doesn't create aorder` parameter.

Multi Page Apps: circular reference when importing app

I define my dash app instance in app.py:

server = flask.Flask(__name__)
app = Dash(__name__,
        plugins=[dl.plugins.pages],
        external_stylesheets=external_stylesheets,
        external_scripts=external_scripts, server=server)

By default the pages plug-in iterates over the modules in the ./pages folder. I have a page that needs
to reference the dash app instance in order to call the get_asset_url() method.

In my page module, the import:

from app import app

Creates a circular reference. I can fix this with an embedded import but I'd prefer not to. Is there a way
to resolve this? Ideally a proxy reference to the current dash instance (dash.current_app) would be one
solution.

Cheers.

[Idea] Support `dl.Input(object.arg)` format

Currently we would do:

import dash_labs as dl
...
div = html.Div()
button = html.Button(children="Click Me")

@app.callback(dl.Output(div, "children"), dl.Input(button, "n_clicks"))
def callback(n_clicks):
    return "Clicked {} times".format(n_clicks)

I feel being able to input the argument directly would be more type-ahead friendly, the Python interpreter will throw an error right away if the argument doesn't exist, and it looks slightly more consistent too.

import dash_labs as dl
...
div = html.Div()
button = html.Button(children="Click Me")

@app.callback(dl.Output(div.children), dl.Input(button.n_clicks))
def callback(n_clicks):
    return "Clicked {} times".format(n_clicks)

Not sure if this is feasible without changing the component structure though (since the arguments are assigned to a value so there's no way to retrace the id of the actual object).

How to specify input vs state

Right now the docs use dx.arg for all inputs/state, defaulting to input but adaptable with kind="state". To create an update button you need to add kind="state" to all the existing inputs and add another like:
n_clicks=dx.arg(html.Button("Update").props["n_clicks"]) (I've omitted the implied kind="input")

(Related point: this is inside the inputs=dict(...) - that might be a little funny since it includes both input and state items, would it be better to call this args?)

The other options that have been floated previously are

  1. dx.input / dx.state - this is slightly more concise but not in the fully-immediate case (which we expect will be the most common case) and requires users to learn about the input/state distinction up front. So overall I probably agree about using dx.arg instead.
  2. a separate argument manual=True that turns all the arguments into State and creates a new button that isn't an argument to the function but is the only triggering Input. For the specific case of delaying all the inputs this is really nice as it's very concise (no need to change every arg) and doesn't require adding an arg to your function that you aren't going to use. But it's very restricted, as you can't mix and match state and input, you can't control what the button looks like.

So kind="input"|"state" does seem like the simplest way to get the flexibility we need. But can we make it easier to change between immediate and on-submit modes? If we implement Trigger inputs in base Dash plotly/dash#1054, maybe we could make a dx.trigger("Update") that creates a button with the given text, and when you add that to your args list it flips the default kind for other args from "input" to "state" (so underneath, the default would be "auto" or something).

We could also give the base Trigger a hidden=True option to omit it from the function args, and perhaps let that be the default if your definition only has a single dx.trigger but turn it off if there are multiple triggers. Or perhaps name your trigger something like _ in the inputs dict and it'll be omitted from the function args?

Too magical? Just trying to find more concise syntax for the common cases.

Todo

  • template arg to layout_from_callback (for DBC vs DDK vs custom)
  • component_from_callback
  • pattern arg to from_callback (for DCC vs DBC vs DDK)
  • labels (kwarg and pack into id)
  • uids
  • error messages

Multi-page app: Page path not working properly on Dash Enterprise

PR (#78) fixed most of the pathing issues (thank you so much!) as the homepage is showing up, but I still have one issue remaining. I have links that reference the various sub-pages on the top of my app and when I click on them they do not do anything (I just stay on the homepage) on Dash Enterprise (works when I run the multi-page app on my local machine).

I can workaround it for now by adding in the app name. So for example:

When clicking on the link on my header I try to go to the link which doesn't work:
https://appserver.host/regional

When I manually add in the name of the app ('capacity') to the address bar and press enter on the below link it does work:
https://appserver.host/capacity/regional

I believe this is an issue with how the page['path'] is served. I am serving up the page['path'] to a dcc.Link object as the href. I think that needs to have added on it the name of the app when hosted on Dash Enterprise.

ddk.Menu( children=[dcc.Link(page["name"], href=page["path"]) for page in dash.page_registry.values() if (not page["path"].startswith("/chapter")) and (page["path"].find("404") == -1)], )

I checked the server logs and the error I receive is below (similar to the one from before):
2022-01-27T08:29:55.407037545Z app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packages/dash/dash.py", line 1336, in dispatch
2022-01-27T08:29:55.407040645Z app[web.1]: response.set_data(func(*args, outputs_list=outputs_list))
2022-01-27T08:29:55.407044045Z app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packages/dash/_callback.py", line 151, in add_context
2022-01-27T08:29:55.407047145Z app[web.1]: output_value = func(*func_args, **func_kwargs) # %% callback invoked %%
2022-01-27T08:29:55.407049945Z app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packages/dash_labs/plugins/pages.py", line 335, in update
2022-01-27T08:29:55.407052945Z app[web.1]: page, path_variables = _path_to_page(app, app.strip_relative_path(pathname))
2022-01-27T08:29:55.407055845Z app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packages/dash/dash.py", line 1562, in strip_relative_path
2022-01-27T08:29:55.407058845Z app[web.1]: return strip_relative_path(self.config.requests_pathname_prefix, path)
2022-01-27T08:29:55.407061645Z app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packages/dash/_utils.py", line 85, in strip_relative_path
2022-01-27T08:29:55.407064545Z app[web.1]: raise exceptions.UnsupportedRelativePath(
2022-01-27T08:29:55.407067345Z app[web.1]: dash.exceptions.UnsupportedRelativePath: Paths that aren't prefixed with requests_pathname_prefix are not supported.
2022-01-27T08:29:55.407070145Z app[web.1]: You supplied: /regional and requests_pathname_prefix was /capacity/

Multi-Page Apps - find a better way to build dash.page_registry

Currently app pages are added to the dash.page_registry when dash.register_page is called. This happens as modules are loaded from the pages/ folder before the app starts. If you try to use dash.page_registry in an app in the pages/ folder, you must use it in a function, otherwise the page registry may not be complete.

Need to find a better way to build dash.page_registry so it's not necessary for people to use a function when trying to use it from within the pages/ folder.

See community discussion here

Description of the workaround here

Dash Express Design Proposal

Overview

After a lot of prototyping, I've come up with a pretty extensive, documentation level, Dash Express design proposal. It's more than I wanted to write on GitHub, so I did it as a hackmd.io document.

Everyone with access to this link should be able to read and comment on the proposal with google doc style comment threads.

https://hackmd.io/zVhQkYXzQUKiiDx6t4A_Sw

Implementation Status

Despite the screenshots and GIFs, I don't have an implementation of this proposal yet. I've basically done enough prototyping to convince myself that it's logically consistent and everything is possible without introducing an unreasonable amount of complexity. And there is code behind all of the GIFs. What I have is a total buggy, untested, mess, and I'm not going to share it in its current form 🙂

After discussion, if we decide to go through with a modified version of this, I'll build a proper implementation to the requirements we settle on.

@nicolaskruchten @chriddyp @alexcjohnson @jackparmer @rpkyle @Marc-Andre-Rivet

Optional dependencies for bootstrap

Rather than having:

pip install dash-bootstrap-components spectra colormath requests tinycss2

It's simpler to write:

pip install dash-labs[bootstrap]

Thoughts?

Rename tpl.layout to children and remove "full" kwarg

After some internal discussion, we've tentatively decided to rename the tpl.layout() method to a tpl.children @property to better emphasize that it's possible to include the output of a template in a larger app (e.g. to make it the children property of a Div).

Along with that, we think it's best to remove the functionality of the full keyword argument to tpl.layout. This argument currently defaults to True, and it has the effect of automatically wrapping the template output in a dbc.Container (for the dbc templates) or ddk.App (for the DDK templates).

This is convenient for simple apps, but it's a bit of magic that could easily trip people up when trying to use templates as part of a larger app (e.g. ending up with multiple ddk.App instances. The cost of removing this functionality is that the user would need to import the corresponding toolkit library and wrap the template result in the appropriate app container. The advantage here is that we think it will make it a lot more clear what the template is actually producing, and how to integrate a template result with a larger app, or how to create an app that consists of multiple templates (e.g. a separate template per tab).

cc @chriddyp @nicolaskruchten @alexcjohnson @AnnMarieW

Discussion on dictionary based callback

Hi,
A few months ago, I wrote a wrapper around callback to support dictionary handing of inputs and outputs in Dash. Unbeknownst to me you all ad a parallel effort. After many months of internal vetting, we determined it was ready for prime time and I submitted it as a PR plotly/dash#1646.

I haven't had a chance to look at the dash-labs approach yet, but the method I propose is very simple to use and intuitive IMHO. It also supports pattern matching. (Took a while to see how to incorporate it). Some documentation is provided in the PR plus I've ported virtually every example in the Dash documentation using this callback. I'd be happy to see how we could potentially take the best of both and create a new better callback.

I've used this extensively inhouse so I'd be happy to show the various ways this can be used. In a way we use it to manage the problem of outputs being driven by many inputs for which the proscribed solution by dash is to create conglomerated callback.

Long callbacks triggered multiple times when running with multiple processes

Hi there,

we're testing the long_callback to issue somewhat long (several seconds) running SQL queries.
When running with gunicorn with multiple workers I noticed that the queries are started/sent multiple times, from different workers when the polling-requests are routed to a worker which hasn't started the callback in the first place.

A tiny sample that reproduces the issue:

import logging
import os
import sys
import time
import dash
import dash_bootstrap_components as dbc
import dash_core_components as dcc
import dash_html_components as html
import dash_labs
import diskcache
from dash_labs import Input, Output
from dash_labs.plugins import DiskcacheCachingCallbackManager
import multiprocessing

# workaround issues with spawn method on macos
multiprocessing.set_start_method("fork")

worker_pid = os.getpid()

cache = diskcache.Cache("./dash-cache")
app = dash.Dash(
    plugins=[
        dash_labs.plugins.FlexibleCallbacks(),
        dash_labs.plugins.HiddenComponents(),
        dash_labs.plugins.LongCallback(DiskcacheCachingCallbackManager(cache)),
    ]
)
wsgi_app = app.server

app.layout = html.Div(
    [
        dbc.Button(id="button-id", children="Run Query!", n_clicks=0, color="primary"),
        html.P(id="paragraph", children="Click the button")
    ]
)

@app.long_callback(
    output=Output("paragraph", "children"),
    args={"n_clicks": Input("button-id", "n_clicks")},
)
def callback(n_clicks):
    if n_clicks == 0:
        return "Click the button"

    print(f"Starting query on worker {worker_pid}")
    time.sleep(5)
    print(f"Query done on worker {worker_pid}")
    return f"Query done on worker {worker_pid}"


if __name__ == "__main__":
    app.run_server(port=8000, debug=True)

Running that with

gunicorn long_callback_test:wsgi_app --access-logfile - --capture-output --log-file - -w 4 -b :8000

produces something like this:

127.0.0.1 - - [13/Aug/2021:12:11:18 +0200] "POST /_dash-update-component HTTP/1.1" 200 344 "http://localhost:8000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36"
Starting query on worker 59201
127.0.0.1 - - [13/Aug/2021:12:11:29 +0200] "POST /_dash-update-component HTTP/1.1" 200 326 "http://localhost:8000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36"
Starting query on worker 59203
127.0.0.1 - - [13/Aug/2021:12:11:30 +0200] "POST /_dash-update-component HTTP/1.1" 200 326 "http://localhost:8000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36"
Starting query on worker 59202
127.0.0.1 - - [13/Aug/2021:12:11:31 +0200] "POST /_dash-update-component HTTP/1.1" 200 326 "http://localhost:8000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36"
127.0.0.1 - - [13/Aug/2021:12:11:32 +0200] "POST /_dash-update-component HTTP/1.1" 200 326 "http://localhost:8000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36"
Starting query on worker 59197
127.0.0.1 - - [13/Aug/2021:12:11:33 +0200] "POST /_dash-update-component HTTP/1.1" 200 326 "http://localhost:8000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36"
Query done on worker 59201
127.0.0.1 - - [13/Aug/2021:12:11:34 +0200] "POST /_dash-update-component HTTP/1.1" 200 344 "http://localhost:8000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36"
Query done on worker 59203
Query done on worker 59202
Query done on worker 59197

I that a known issue/limitation with the diskcache or am I doing something wrong here?
Would using the celery backend solve this?

Idea: Specify input props in arg wrapper

Possible alternative to ideas discussed in #5 and #6.

If we drop support for the ipywidget-style patterns (which some folks seem more comfortable with), then it would be logically consistent to specify the component properties as a keyword argument to dx.arg.

dx.callback(
    app,
    args=dict(
        single_date=dx.arg(dcc.DatePickerSingle(), props="date", label="Single Date"),
        date_range=dx.arg(
            dcc.DatePickerRange(), props=("start_date", "end_date"), label="Date Range", kind="state"
        ),
    ),
    args_out=dx.arg(dcc.Markdown(), props="children"),
)
def callback(single_date, date_range):
    start_date, end_date = date_range
    return "-".join([single_date, start_date, end_date])

This example also shows using the args/args_out keywords to dx.callback instead of inputs/output because of the incompatibility discussed in #6 (comment).

The dx.arg wrapper could include a default of props="value". If we wanted to push toward the explicit side of the spectrum, we could require the dx.arg wrapper. Combined with the props="value" default, this would make the default choice of using the "value" prop less magical.

cc @nicolaskruchten @alexcjohnson @chriddyp

build_id creates IDs that are extremely hard to use with selenium

The current dynamic id implementation is fantastic, but is very hard to use with the selenium selectors used in dash.testing.
In "normal" dash, we expect component ids to be str values which are directly mapped to a component id.
This allows you to use dash_duo.driver.find_element_by_id to select page element corresponding to dash components.

However, the dynamic ids created by build_id are dictionaries which are string-serialized into component ids.
I believe occurs in the dash frontend at: https://github.com/plotly/dash/blob/7afb2edbccff0af98f87df083dd6fbedece10463/dash/dash-renderer/src/actions/dependencies.js#L114-L123

This means that:

a) The css component ID, for use in selectors, isn't easily derived from the python component's .id property as it depends on the dict's serialization format.
b) The serialized dict requires CSS identifier escaping to be used in selenium selectors.

This breaks the existing dash_duo.driver.find_element_by_id and dash_duo.wait_for_element_by_id interfaces.

Neither of these are insurmountable, but it would be preferable if there was a "simple" way of accessing the "css-selector-id" of the component id.

Potential options could include:

a) Returning a canonical string id from build_id.
b) Implementing an equivalent to the existing stringifyID function in python, so that it can be applied to python identifiers.

Either case will require CSS selector escaping for any special characters, which could be handled via soupsieve.escape.

Multi Page Apps: Issue with Dynamic Layouts

I'm having an issue using dynamic layouts with dash lab's pages/ plugin. I'm trying to use a dynamic layout to fetch new data when the user refreshes the page as detailed in the docs. However, I keep receiving the error message shown below.

Screen Shot 2022-01-27 at 11 48 50 PM

The only way I can get around this error is setting app.layout equal to the function call, "serve_layout()", instead of the function instance, "serve_layout". I don't understand why my "fix" worked, and from what I can tell in my testing, the layout is no longer dynamic and does not respond to changes in the data source when I refresh the page. I've included a simple example that just requires an empty pages/ directory in addition to the code below.

from dash import Dash, html, dcc
import dash
import dash_labs as dl

app = Dash(__name__, plugins=[dl.plugins.pages])

dash.register_page("another_home", layout="We're home!", path="/")
dash.register_page(
    "very_important", layout="Don't miss it!", path="/important", order=0
)

def serve_layout():
    return html.Div(
    [
        html.H1("App Frame"),
        html.Div(
            [
                html.Div(
                    dcc.Link(f"{page['name']} - {page['path']}", href=page["path"])
                )
                for page in dash.page_registry.values()
                if page["module"] != "pages.not_found_404"
            ]
        ),
        dl.plugins.page_container,
    ]
)

app.layout = serve_layout

if __name__ == "__main__":
    app.run_server(debug=True)

Multi-Page Apps [Feature request] Make Dash SEO-friendly

A React app that is SEO-friendly out of the box would be a big deal, especially with support for multi-page apps and URLs.

The main code that converts the layout of the app to HTML is already done, and is being used in the Dash docs website.

We basically parse the app’s layout and recursively convert it to HTML. Then we modify the app’s index_string with the Dash.interpolate_index function.

The only trick I can see is if developers make changes to the app’s index_string, and we need to make sure we don’t make changes that conflict with theirs.

If this sounds interesting, we can explore this further, and I'm happy to help whichever way I can.

Thanks!

Multi-page app: Working on pythonanywhere but not Dash Enterprise

Multi-page app deployed successfully to pythonanywhere using dash-labs==1.0.1.
When deploying the app (name of the app in Dash Enterprise is 'capacity') the app will load (using dash-labs==1.0.1) but will not display the different pages. It just loads the contents of the app.py file.
Seems like an issue with the path (supplied '/' and the requests_pathname_prefix was '/capacity/').

See below for the error:
2022-01-26T02:45:42.302776025Z app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packages/dash_labs/plugins/pages.py", line 265, in update
2022-01-26T02:45:42.302781225Z app[web.1]: if path_id == app.strip_relative_path(page["path"]):
2022-01-26T02:45:42.302802625Z app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packages/dash/dash.py", line 1562, in strip_relative_path
2022-01-26T02:45:42.302808125Z app[web.1]: return strip_relative_path(self.config.requests_pathname_prefix, path)
2022-01-26T02:45:42.302828025Z app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packages/dash/_utils.py", line 85, in strip_relative_path
2022-01-26T02:45:42.302833425Z app[web.1]: raise exceptions.UnsupportedRelativePath(
2022-01-26T02:45:42.302838625Z app[web.1]: dash.exceptions.UnsupportedRelativePath: Paths that aren't prefixed with requests_pathname_prefix are not supported.
2022-01-26T02:45:42.302843825Z app[web.1]: You supplied: / and requests_pathname_prefix was /capacity/

Using State with long_callback

Hey there,

Can we use State with long callbacks?. I have something that looks like this:

@app.callback(
        Output("session", "data"),
        Input("new-analysis-submit-button", "n_clicks"),
        [
            State("session", 'data'),
            State("protein-name", 'value'),
            State("kmer-length", 'value'),
            State("support-threshold", 'value'),
            State("header-format", 'value'),
            State("sequences-file", 'contents')
        ]
    )
def fn(...): ...

This function takes a while to finish up, so I would really like to use long_callback, but as far as I can tell it does not have support for State. Do we have a workaround?

Picking input prop(s)

For changing the prop of an input from the default value, right now we have eg:

dcc.DatePickerSingle().props["date"]
dcc.DatePickerRange().props[("start_date", "end_date")]

The great thing about this syntax vs dcc.DatePickerSingle().date is you can easily pick multiple values and pack them into either a list or a dict for your function - and we should allow you to do either of those with the regular id/prop callback definitions too (though list packing would probably have to be a breaking change due to how we unlisted outputs, inputs, and state in dash 1.x)

I don't get why it's formatted to look like a dict though - to me it's more like a method modifying the component's "value" to be some prop(s) other than value.

Also: what if you want one prop as input and another as state? For example a dcc.Store with data as state, modified_timestamp as input? And per #5, what if you want modified_timestamp to be a Trigger, or hidden? We could imagine making tuples:
dcc.Store().props(("modified_timestamp", "state"), "data")
or take advantage of the fact that props can't have dots in them:
dcc.Store().props("modified_timestamp.hidden", "data")

Enhancement: Consider support wrapping number around input text

For number based text input, there are up and down arrows allowing user to click and change the value in the input. However, when it is a constrained number with min, max and step, I've noticed that there is no quick way for user to jump from min to max value i.e. wrapping around min to max value. My suggestion on top of existing min, max and step, Dash could provide a new "wraparound" attribute so that user could traverse from min to max number should all min, max and step values are provided.

Issue with getting state/value of a dcc element which was inputted another callback

Hey,

I am a bit confused on how to properly chain callbacks so that I would be able to access the values of an element outside the original input callback.
Example:

serial_number_options =dcc.Dropdown(id="serial")
ecu_type_options=dcc.Dropdown(id="ecu")

@app.callback(
output=[dl.Output(serial_number_options,"options")],
args=dict(
test_station_id=dl.Input(test_id_options),
),
template=tpl
)def func():
return some list of values to store in serial_number options

@app.callback(
output=[dl.Output(ecu_type_options,"options")],
args=dict(
serial_number=dl.Input(serial_number_options),
),
template=tpl
)def func2():
read in the selected value of serial_number_options drop down
return some list of values to store in ecu_type_options

Because each time I run something like this I just get the following error:
"dash.exceptions.DuplicateIdError: Duplicate component id found in the initial layout: serial"

dash-labs not support python 3.8 yet?

I've run some dash-labs examples and get error called

File "/Users/bytedance/opt/anaconda3/lib/python3.8/multiprocessing/reduction.py", line 60, in dump
ForkingPickler(file, protocol).dump(obj)
AttributeError: Can't pickle local object 'make_update_cache.._callback'

My version is
python 3.8.5
dash 1.20.0
dash-bootstrap-components 0.12.2

Updating page_registry from a callback

Hello,
I think I found a bug when attempting to update the page_registry from a callback. The main issue I’ve been facing with this approach is the redirects do not seem to work. Inspecting the page_registry shows that the redirect_from parameter has the proper list of values (e.g. in the code below 'redirect_from': ['/callback-page']). But entering this in the url always returns the 404 page. However, when using the redirects with a page defined outside a callback, it works without issue.

I worked up a quick example demonstrating the issue.

import dash
from dash import html, Output, Input
import dash_labs as dl

import dash_bootstrap_components as dbc

app = dash.Dash(__name__, plugins=[dl.plugins.pages])
server = app.server

dash.register_page(
    "historical_analysis",
    path="/historical-analysis",
    redirect_from=["/test"],
    layout=html.Div(["Historical Analysis Page"]),
)
dash.register_page(
    "forecast",
    path="/forecast",
    layout=(html.Button("Button", id="button"), html.Div(id="content")),
)

app.layout = dbc.Container(
    [
        dbc.NavbarSimple(
            [
                dbc.NavItem(dbc.NavLink(page["name"], href=page["path"]))
                for page in dash.page_registry.values()
            ]
        ),
        dl.plugins.page_container,
    ]
)


@app.callback(
    Output(component_id="content", component_property="children"),
    Input(component_id="button", component_property="n_clicks"),
    prevent_initial_call=True,
)
def update_output_div(n_clicks):

    dash.register_page(
        "callback_page",
        path=f"/callback-page/{n_clicks}",
        redirect_from=["/callback-page"],
        layout=html.Div(["Callback Page"]),
    )
    return f"Output: {n_clicks}"


if __name__ == "__main__":
    app.run_server(debug=True)

And here is a short video demonstrating the unexpected behavior:
Screen Recording 2022-03-06 at 6 20 06 PM

My main use case for this is with dash enterprise's dashboard engine. When the user saves a dashboard, it creates a unique snapshot_id and I generate new pages for viewing and editing this dashboard.

Thanks in advance for any help!

long_callback: Make `set_progress` arguments more flexible

See https://community.plotly.com/t/dash-labs-0-3-0-app-long-callback-support/53719/4?u=jmmease.

Support workflows like:

@app.long_callback(
    ...,
    progress=dict(
        progress1=dl.Output("component1", "attribute1"),
        progress2=dl.Output("component2", ("attribute1", "attribute2"))
    )
)
def my_function(set_progress, *args, **kwargs):
    # Do something
    set_progress({"progress1": ..., "progress2": ...})
    # Do some more stuff
    set_progress({"progress1": ..., "progress2": ...})
   # And some more
    return ...

Multi-Page Apps [Feature Request] Prevent ID collisions

A common challenge when making multi-page apps is ensuring that IDs are unique in the entire app.

Perhaps there is a way to handle this within the pages/ api, for example using uuid or a page specific string as a prefix to the ids on each page.

See the community discussion here

And a possible solution from @emilhe in the dash-extensions library as demonstrated here

How to update the inline CSS in templates in V 0.3.0?

Some of the inline CSS in the templates will override styles set in an app. For example, when using a template with a Bootstrap theme, it's not possible to change the background and text color in the DashTable with conditional formatting or custom CSS in the assets folder.

Also, when using a template as part of a larger app, the CSS of the template will affect the entire app, not just the style in the template. Take the case where you use a DbcCard to display only a figure with a slider within a larger app. If there is a DataTable in a different part of the app, it will be styled according to the inline CSS of the DbcCard.

In dash-labs v 0.1.0 it was possible to override the inline CSS in the templates with:
tpl._inline_css =""

However, as of dash-labs v 0.3.0 that doesn't work. Is there some other way to update the template's inline CSS?



dash-labs v 0.3.0 conditional formatting does not work.
Note that the table is outside of the template container and it is styled based on the inline CSS of DbcCard

image

#   Use with dash-labs=0.3.0
#==================================

import dash
import dash_table
import pandas as pd
import dash_labs as dl
import dash_bootstrap_components as dbc


app = dash.Dash(__name__, plugins=[dl.plugins.FlexibleCallbacks()])
tpl = dl.templates.DbcCard(app, title="Simple App", columns=4)

# This has no effect - it's not possible to exclude dash-labs inline_css
# Conditional formatting in DashTable does not work
tpl._inline_css=""


df = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/solar.csv")

app.layout = dbc.Container(
    [
        dash_table.DataTable(
            data=df.to_dict("records"),
            columns=[{"name": i, "id": i} for i in df.columns],
            style_data_conditional=[
                {
                    "if": {
                        "filter_query": "{{Number of Solar Plants}} = {}".format(
                            df["Number of Solar Plants"].min()
                        ),
                    },
                    "backgroundColor": "#FF4136",
                    "color": "white",
                }
            ],
        ),
        tpl.children
    ]
)

if __name__ == "__main__":
    app.run_server(debug=True)

dash-labs v 0.1.0 conditional formatting works when inline CSS is removed.
image

#   Use with dash-labs=0.1.0
#==================================

import dash
import dash_table
import pandas as pd
import dash_labs as dl
import dash_bootstrap_components as dbc


app = dash.Dash(__name__, plugins=[dl.plugins.FlexibleCallbacks()])
tpl = dl.templates.dbc.DbcCard(
    title="Sample App", figure_template=True, theme=dbc.themes.PULSE
)

# This excludes the dash-labs inline css - which allows conditional formatting to work
tpl._inline_css = ""

df = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/solar.csv")

app.layout = dbc.Container(
    [
        dash_table.DataTable(
            data=df.to_dict("records"),
            columns=[{"name": i, "id": i} for i in df.columns],
            style_data_conditional=[
                {
                    "if": {
                        "filter_query": "{{Number of Solar Plants}} = {}".format(
                            df["Number of Solar Plants"].min()
                        ),
                    },
                    "backgroundColor": "#FF4136",
                    "color": "white",
                }
            ],
        ),
        tpl.layout(app),
    ]
)

if __name__ == "__main__":
    app.run_server(debug=True)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.