> Version 0.6 brings DashMachine one big step forward to being a finished product by adding a gui to edit the various settings in the config.ini. **Changelog** - improvements to /home including 'pinned' cards, multi-select tag filtering, 'action providers' allowing you to do web searches from the searchbar - rebuilt sidenav with list view, mirroring filter/search/collapse state of the homepage - /settings and /home now on same route - dynamic reloading of settings (no more page reloads) - dedicated config.ini editor slide-out - settings editor slide-out - card editor slide-out - better access group control - dedicated documentation pages - improved documentation - new system for automatically generating documentation for platforms - ability to load custom platforms - added an 'on_starup' method for platforms allowing for registering api routes. (example coming soon)
8.9 KiB
Creating Platforms
DashMachine platforms are python plugins loaded by Python's importlib
module. When they are loaded by DM, they are initialized with their Platform
class. The official platforms for dashmachine are located at DashMachine/dashmachine/platform
and are a great resource for learning how they work.
NOTE: When you modify or add one of the files in
DashMachine/dashmachine/platform
, changes will be wiped out by updates. If you want to experiment with your own platforms, put them inDashMachine/dashmachine/user_data/platform
to persist updates.
A minimal platform
Let's start by making a super simple platform. It's just going to return 'Hello World' to the card's data source container. First add a file called hello_world.py
in DashMachine/dashmachine/user_data/platform
with the contents:
class Platform:
def docs(self):
# This is the function DM calls to get the metadata about your plaform.
# This is what DM uses to generate the documentation and options in the gui forms.
# It is very important that this information is correct.
documentation = {
"name": "hello_world",
"author": "RMountjoy",
"author_url": "https://github.com/rmountjoy92",
"version": 1.0,
"description": "return 'Hello World' to the card's data source container",
"returns": "Hello World",
"variables": [
{
"variable": "[variable_name]",
"description": "Name for the data source.",
"default": "",
"options": ".ini header",
},
{
"variable": "platform",
"description": "Name of the platform.",
"default": "hello_world",
"options": "hello_world",
},
],
}
return documentation
def __init__(self, *args, **kwargs):
# This is the initialization function.
# This should be reserved for setting up the platform's configuration.
# NOTE: this is not where you call APIs, as this code is run every time DM
# initializes your 'Platform' class (e.g. to pull documentation, etc).
# These following two lines parses the options supplied by the user in their config.ini
for key, value in kwargs.items():
self.__dict__[key] = value
def process(self):
# this is the function that is ran when the web client requests
# information from this platform (e.g. when the dashboard is loaded)
# this is where you call APIs, or whatever you're doing to get data.
return "Hello World"
NOTE: After you add a new platform file to your user_data/platforms folder, you will need to restart DashMachine for the platform to be available.
Then, we would add the following two entries to our config.ini NOTE All custom platforms must be prefixed with custom_
when they are referenced in a data_source
entry:
[hello_world_ds]
platform = custom_hello_world
[hello_world]
type = custom
data_sources = hello_world_ds
Now, on your dashboard, you should see this:
Ugly right!? Which leads us to the next section,
Rendering HTML
Let's make some changes to our hello_world.py
file, at the top of the file we would add:
from flask import render_template_string
then we would change our process()
method like so:
def process(self):
text_to_display = "Hello World"
html_string = """
<div class="row center-align">
<div class="col s12">
<i class="material-icons-outlined theme-primary-text medium">language</i>
<h5 class="font-weight-900">{{ text_to_display }}</h5>
</div>
</div>
"""
return render_template_string(html_string, text_to_display=text_to_display)
Much better:
For a full reference of the html elements you have available to you, check out:
Remeber that your returned HTML can include script
and style
tags for including js/jquery/css
Utilizing user configuration
Okay, let's say that instead of displaying our fixed 'Hello World' message, we'll display the text_to_display
variable from their configuration. So the entries in the .ini file would look like:
[hello_world_ds]
platform = custom_hello_world
text_to_display = Hi there.
[hello_world]
type = custom
data_sources = hello_world_ds
and in our hello_world.py
file the only thing we would have to change is:
return render_template_string(html_string, text_to_display=text_to_display)
changes to:
return render_template_string(html_string, text_to_display=self.text_to_display)
So, now our card displays whatever text_to_display
is set to:
NOTE: Now that we have added a configuration option, we need to update our docs()
method, otherwise our new option will not show up in gui forms:
def docs(self):
documentation = {
"name": "hello_world",
"author": "RMountjoy",
"author_url": "https://github.com/rmountjoy92",
"version": 1.0,
"description": "return the value of 'text_to_display' to the card's data source container",
"returns": "text_to_display as formatted html",
"variables": [
{
"variable": "[variable_name]",
"description": "Name for the data source.",
"default": "",
"options": ".ini header",
},
{
"variable": "platform",
"description": "Name of the platform.",
"default": "hello_world",
"options": "hello_world",
},
{
"variable": "text_to_display",
"description": "the text to display on the card",
"default": "Hello World",
"options": "string",
},
],
}
return documentation
You'll notice on the new variable we just made that there is a default
field. We need to make sure we have defaults set up for our configuration options, so users don't have to fill out optional configurations. To do so, we would rewrite our __init__()
method like so:
def __init__(self, *args, **kwargs):
for key, value in kwargs.items():
self.__dict__[key] = value
if not hasattr(self, "text_to_display"):
self.text_to_display = "Hello World"
To sum it all up, here is our user configurable version of our hello world platform:
from flask import render_template_string
class Platform:
def docs(self):
documentation = {
"name": "hello_world",
"author": "RMountjoy",
"author_url": "https://github.com/rmountjoy92",
"version": 1.0,
"description": "return the value of 'text_to_display' to the card's data source container",
"returns": "text_to_display as formatted html",
"variables": [
{
"variable": "[variable_name]",
"description": "Name for the data source.",
"default": "",
"options": ".ini header",
},
{
"variable": "platform",
"description": "Name of the platform.",
"default": "hello_world",
"options": "hello_world",
},
{
"variable": "text_to_display",
"description": "the text to display on the card",
"default": "Hello World",
"options": "string",
},
],
}
return documentation
def __init__(self, *args, **kwargs):
for key, value in kwargs.items():
self.__dict__[key] = value
if not hasattr(self, "text_to_display"):
self.text_to_display = "Hello World"
def process(self):
html_string = """
<div class="row center-align">
<div class="col s12">
<i class="material-icons-outlined theme-primary-text medium">language</i>
<h5 class="font-weight-900">{{ text_to_display }}</h5>
</div>
</div>
"""
return render_template_string(html_string, text_to_display=self.text_to_display)
and in our config.ini:
[hello_world_ds]
platform = custom_hello_world
text_to_display = Hi there.
[hello_world]
type = custom
data_sources = hello_world_ds