Skip to content

Commit

Permalink
Merge pull request #4 from mfaafm/feature/safe_name
Browse files Browse the repository at this point in the history
Feature/safe_name, release v0.3.0
  • Loading branch information
mfaafm committed Dec 15, 2019
2 parents 9a11661 + 767d47f commit 71adc37
Show file tree
Hide file tree
Showing 12 changed files with 112 additions and 55 deletions.
19 changes: 10 additions & 9 deletions .bumpversion.cfg
@@ -1,20 +1,21 @@
[bumpversion]
current_version = 0.2.0
current_version = 0.3.0
commit = True
tag = True

[bumpversion:file:setup.py]
search = version='{current_version}'
replace = version='{new_version}'
search = version="{current_version}"
replace = version="{new_version}"

[bumpversion:file:README.rst]
search = v{current_version}.
replace = v{new_version}.
search = v{current_version}
replace = v{new_version}

[bumpversion:file:docs/conf.py]
search = version = release = '{current_version}'
replace = version = release = '{new_version}'
search = version = release = "{current_version}"
replace = version = release = "{new_version}"

[bumpversion:file:src/dasher/__init__.py]
search = __version__ = '{current_version}'
replace = __version__ = '{new_version}'
search = __version__ = "{current_version}"
replace = __version__ = "{new_version}"

5 changes: 5 additions & 0 deletions CHANGELOG.rst
@@ -1,6 +1,11 @@
Changelog
=========

0.3.0 (2019-12-15)
------------------
* Generate `id` property from `name` for every callback. The `id` is now used to
identify the callback, while `name` is used in the layout for displaying.

0.2.0 (2019-11-03)
------------------
* Use cookiecutter to create a proper project structure.
Expand Down
2 changes: 2 additions & 0 deletions CONTRIBUTING.rst
Expand Up @@ -53,6 +53,8 @@ To set up `dasher` for local development:

tox

Further information on developing, can be found in the `cookiecutter template <https://github.com/ionelmc/cookiecutter-pylibrary>`_
that was used to set up this project.
5. Commit your changes and push your branch to GitHub::

git add .
Expand Down
8 changes: 4 additions & 4 deletions README.rst
Expand Up @@ -46,9 +46,9 @@ Overview
:alt: Supported implementations
:target: https://pypi.org/project/dasher

.. |commits-since| image:: https://img.shields.io/github/commits-since/mfaafm/dasher/v0.2.0.svg
.. |commits-since| image:: https://img.shields.io/github/commits-since/mfaafm/dasher/v0.3.0.svg
:alt: Commits since latest release
:target: https://github.com/mfaafm/dasher/compare/v0.2.0...master
:target: https://github.com/mfaafm/dasher/compare/v0.3.0...master



Expand Down Expand Up @@ -97,7 +97,7 @@ Creating a simple, interactive dashboard with a nice layout is as easy as this::

The resulting dashboard looks like this:

.. image:: https://raw.githubusercontent.com/mfaafm/dasher/v0.2.0/docs/images/hello_world.gif
.. image:: https://raw.githubusercontent.com/mfaafm/dasher/v0.3.0/docs/images/hello_world.gif
:alt: hello world example
:align: center

Expand All @@ -112,7 +112,7 @@ License

Free software, `MIT License`_

.. _`MIT License`: https://raw.githubusercontent.com/mfaafm/dasher/v0.2.0/LICENSE
.. _`MIT License`: https://raw.githubusercontent.com/mfaafm/dasher/v0.3.0/LICENSE

Development
===========
Expand Down
55 changes: 30 additions & 25 deletions docs/conf.py
Expand Up @@ -4,44 +4,49 @@
import os

extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.autosummary',
'sphinx.ext.coverage',
'sphinx.ext.doctest',
'sphinx.ext.extlinks',
'sphinx.ext.ifconfig',
'sphinx.ext.napoleon',
'sphinx.ext.todo',
'sphinx.ext.viewcode',
"sphinx.ext.autodoc",
"sphinx.ext.autosummary",
"sphinx.ext.coverage",
"sphinx.ext.doctest",
"sphinx.ext.extlinks",
"sphinx.ext.ifconfig",
"sphinx.ext.napoleon",
"sphinx.ext.todo",
"sphinx.ext.viewcode",
]
source_suffix = '.rst'
master_doc = 'index'
project = 'dasher'
year = '2019'
author = 'Martijn Arts'
copyright = '{0}, {1}'.format(year, author)
version = release = '0.2.0'
source_suffix = ".rst"
master_doc = "index"
project = "dasher"
year = "2019"
author = "Martijn Arts"
copyright = "{0}, {1}".format(year, author)
version = release = "0.3.0"

pygments_style = 'trac'
templates_path = ['.']
pygments_style = "trac"
templates_path = ["."]
extlinks = {
'issue': ('https://github.com/mfaafm/dasher/issues/%s', '#'),
'pr': ('https://github.com/mfaafm/dasher/pull/%s', 'PR #'),
"issue": ("https://github.com/mfaafm/dasher/issues/%s", "#"),
"pr": ("https://github.com/mfaafm/dasher/pull/%s", "PR #"),
}
# on_rtd is whether we are on readthedocs.org
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
on_rtd = os.environ.get("READTHEDOCS", None) == "True"

if not on_rtd: # only set the theme if we're building docs locally
html_theme = 'sphinx_rtd_theme'
html_theme = "sphinx_rtd_theme"

html_use_smartypants = True
html_last_updated_fmt = '%b %d, %Y'
html_last_updated_fmt = "%b %d, %Y"
html_split_index = False
html_sidebars = {
'**': ['searchbox.html', 'globaltoc.html', 'sourcelink.html'],
"**": ["searchbox.html", "globaltoc.html", "sourcelink.html"],
}
html_short_title = '%s-%s' % (project, version)
html_short_title = "%s-%s" % (project, version)

napoleon_use_ivar = True
napoleon_use_rtype = False
napoleon_use_param = False

linkcheck_ignore = [
r"https://raw.githubusercontent.com/mfaafm/dasher/v+",
r"https://github.com/mfaafm/dasher/compare/v+",
]
4 changes: 2 additions & 2 deletions setup.cfg
Expand Up @@ -2,7 +2,7 @@
universal = 1

[flake8]
max-line-length = 140
max-line-length = 88
exclude = */migrations/*

[tool:pytest]
Expand All @@ -28,7 +28,7 @@ testpaths =

[tool:isort]
force_single_line = True
line_length = 120
line_length = 88
known_first_party = dasher
default_section = THIRDPARTY
forced_separate = test_dasher
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -24,7 +24,7 @@ def read(*names, **kwargs):

setup(
name="dasher",
version="0.2.0",
version="0.3.0",
license="MIT",
description="Generate interactive plotly dash dashboards in an instant",
long_description="%s\n%s"
Expand Down
2 changes: 1 addition & 1 deletion src/dasher/__init__.py
Expand Up @@ -2,6 +2,6 @@
from .app import Dasher
from .base import CustomWidget

__version__ = "0.2.0"
__version__ = "0.3.0"

__all__ = ["Dasher", "Api", "CustomWidget"]
24 changes: 22 additions & 2 deletions src/dasher/api.py
Expand Up @@ -5,6 +5,7 @@
from dash.dependencies import Output

from dasher.base import BaseLayout
from dasher.base import generate_callback_id


class Api(object):
Expand Down Expand Up @@ -252,8 +253,27 @@ def register_callback(app, callback):
Parameters
----------
app: dash.Dash
The dash app
The dash app.
callback: DasherCallback
The dasher callback to register
The dasher callback to register.
"""
return app.callback(callback.outputs, callback.inputs)(callback.f)

@staticmethod
def generate_callback_id(name):
""" Get callback id from `name`.
It is a lowercase version of name, where all non-alphanumeric characters are
replaced by underscores.
Parameters
----------
name: str
The callback `name` to generate an id from.
Returns
-------
str
Lowercase version of `name`, where all non-alphanumeric characters are
replaced by underscores.
"""
return generate_callback_id(name)
8 changes: 5 additions & 3 deletions src/dasher/app.py
Expand Up @@ -125,9 +125,11 @@ def callback(self, _name, _desc=None, _labels=None, _layout_kw=None, **kwargs):
def function_wrapper(f):
layout = _layout_kw if _layout_kw is not None else {}

widgets = self.api.generate_widgets(kwargs, _labels, _name)
callback_id = self.api.generate_callback_id(_name)

widgets = self.api.generate_widgets(kwargs, _labels, callback_id)
outputs, inputs = self.api.generate_dependencies(
widgets, f"{self.api.layout.output_base}-{_name}"
widgets, f"{self.api.layout.output_base}-{callback_id}"
)

callback = Callback(
Expand All @@ -141,7 +143,7 @@ def function_wrapper(f):
inputs=inputs,
layout_kw=_layout_kw,
)
self.callbacks[callback.name] = callback
self.callbacks[callback.id] = callback

self.api.layout.add_callback(callback, self.app, **layout)
return self.api.register_callback(self.app, callback)
Expand Down
22 changes: 22 additions & 0 deletions src/dasher/base.py
@@ -1,10 +1,30 @@
import re
from abc import ABC
from abc import abstractmethod
from collections import OrderedDict

from dash.development.base_component import Component


def generate_callback_id(name):
""" Get callback id from `name`.
It is a lowercase version of name, where all non-alphanumeric characters are
replaced by underscores.
Parameters
----------
name: str
The callback `name` to generate an id from.
Returns
-------
str
Lowercase version of `name`, where all non-alphanumeric characters are replaced
by underscores.
"""
return re.sub(r"\W+", "_", name).lower()


class BaseWidget(ABC):
""" Abstract base class of a dasher widget.
A dasher widget is an interactive control, which consists of an interactive dash
Expand Down Expand Up @@ -86,6 +106,7 @@ class CustomWidget(object):
The attribute used for the ``dash.dependencies.Input`` dependency.
Default: "value".
"""

def __init__(self, component, dependency="value"):
if not isinstance(component, Component):
msg = "component must be a dash.development.base_component.Component"
Expand Down Expand Up @@ -230,6 +251,7 @@ class Callback(object):
def __init__(
self, name, description, f, kw, labels, widgets, outputs, inputs, layout_kw
):
self.id = generate_callback_id(name)
self.name = name
self.description = description
self.f = f
Expand Down
16 changes: 8 additions & 8 deletions src/dasher/layout/bootstrap/layout.py
Expand Up @@ -138,10 +138,10 @@ def render_card(self, callback, **kwargs):

cols = [dbc.Col(w.layout) for w in callback.widgets]
rows = [dbc.Row(row) for row in self._chunks(cols, widget_cols)]
widgets_form = dbc.Form(rows, id=f"{self.widgets_base}-{callback.name}")
widgets_form = dbc.Form(rows, id=f"{self.widgets_base}-{callback.id}")

output = dbc.Container(
id=f"{self.output_base}-{callback.name}", style={"marginTop": "1em"}
id=f"{self.output_base}-{callback.id}", style={"marginTop": "1em"}
)

card_header = dbc.CardHeader(callback.name)
Expand All @@ -163,7 +163,7 @@ def add_callback(self, callback, app, **kwargs):
**kwargs:
Keyword arguments to override default layout settings for a callback.
"""
tab = dbc.Tab(label=callback.name, tab_id=callback.name)
tab = dbc.Tab(label=callback.name, tab_id=callback.id)

if len(self.callbacks) == 0:
self.tabs = dbc.Tabs(
Expand All @@ -185,20 +185,20 @@ def add_callback(self, callback, app, **kwargs):

content = self.render_card(callback, **kwargs)

self.callbacks[callback.name] = callback
self.callbacks[callback.id] = callback
callback.layout = content

def render_callback(self, name):
def render_callback(self, id):
""" Callback method to switch between tabs.
Parameters
----------
name: str
Name of the callback to render.
id: str
ID of the callback to render.
Returns
-------
dash.development.base_component.Component
Layout of the callback.
"""
return self.callbacks[name].layout
return self.callbacks[id].layout

0 comments on commit 71adc37

Please sign in to comment.