Skip to content
Draft
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 .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[submodule "dashboard/dashboard_components"]
path = dashboard/dashboard_components
url = git@github.com:lanalabs/dashboard_components.git
[submodule "minimalistic_dashboard/dashboard_components"]
path = minimalistic_dashboard/dashboard_components
url = git@github.com:lanalabs/dashboard_components.git
136 changes: 109 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,113 @@
develop.sh
emphaise what partial results and where can be accessed

# Python Dashboard for Lana PM
A documented example for python dashboards that can be used as a starting point for any new dashboards. The example shows a multipage app with a rather simple page 1 and a more complicated page 2. Page one can be seen as an example of how to create simple dashboard pages with elements being mostly independent of each other. Page 2 shows a complicated case where a table can be updated by the user, which then calculates the cost and updates a KPI below. Additionally a graph shows the occurences of the event selected in the table. For documentation of how to create plots, we refer to the documentation of Ploty and Dash. For an explanation of how to make api requests to the backend please see the Pylana Documentation and the api specifications of Lana Process Mining.

## Getting started
### Install requirements
Create a new conda environment using:
`conda create env --env name`
A documented example for python dashboards that can be used as a starting point
for any new dashboards. An example project _dashboards_ shows a multipage app
with a rather simple page-1 and a more complex page-2.

* Page-1 can be seen as an example of how to create simple dashboard page with
elements being mostly independent of each other.

* Page 2 shows a more complex case where a table can be updated by the user,
which then calculates the cost and updates a KPI below.

Additionally a graph shows the occurences of the event selected in the table.
For documentation of how to create plots, we refer to the documentation of Plotly
and Dash. For an explanation of how to make api requests to the backend please
see the Pylana documentation and the api specifications of Lana Process Mining.

Additionally, a rather minimalistic dashboard is also provided by the project
_minimalistic\_dashboard_, which is potentially the simplest example that
involves the smallest set of external dependencies.

## Getting started for a local setup without Process Mining front-end

There are two potential ways to choose from for developing any dashboard, in
order to run the desired code:
- Running the entire system via docker-compose on a local computer. This
involves to be able to run _Process Mining_ as a whole.
- Developing the dashboard code independently and uploading the code
incrementally over and over again until ready to some remotely available
setup. (not recommended)

### Preparing a (your) local system
for using Python

Make sure the submodules are pulled and up-to-date on the example project of
your interest, execute in the root of this repository:

`git submodule update --init --recursive`

Create a new conda environment using in one of the example project folder:

`conda-shell`
`conda create -n <env-name>`

where _<env-name>_ is the name of the conda environment to be used.

Activate that environment using:

Activate that environment using:
`conda activate <env-name>`

Install required packages:
`conda install requirements.txt`
`conda install lana_listener-0.0.1.tar.gz`
Install required packages:

`conda install -u`

`pip install pylana`
`pip install lana_listener-0.0.1.tar.gz`

Where _pylana_ is available in public repository;
_lana-listener_ component shall be sourced as a ready made package from lanalabs/github.

One may use a _requirements.txt_ to install generic dependencies into the
particular conda environment that is hosted natively on the local system in
order to develop the dashboard. Note that, these installed dependencies will not
be carried over onto the target system as installed ones!

When the setup of the local system and the remote system diverges, e.g.
supported python versions, available dependencies, etc; that may result in
failure.

A __requirements.txt_ file may be used, that is defined in the mining repository's
container setup that is actually used for production setup.

### Setting up configs and running the dashboard locally
It is possible to run the dashboard independent of LANA PM.
Simply fill in the API key and log_id into the `lana_listener` object and start `index.py`.

### Uploading your first dashboard
Simply follow the steps in the jupyter-notebook to create a dashbaord and link it to the log. Once you have the ```dashboard_id``` and it is connected to the log, you will be able to upload source code either using the notebook, or using the ```upload.sh``` script.
For a detailed instruction of how to upload also other types of dashboards, as well as a Bash and Python guide to upload, please see ```UploadTutorial.pdf```.

### Uploading (new) source-code
First stop tracking the ```upload.sh``` file so that you can change the file without pushing the changes to git by accident.
```git update-index --assume-unchanged upload.sh```
After filling in the api_key, dashboard_id and url, open the terminal and run
```bash upload.sh```

It is possible to run the dashboard independent of LANA PM.
Simply fill in the API key and log_id into the `lana_listener` object and start
`index.py` in your own python (potentially _conda_) environment.

### Deploying your dashboard

Simply follow the steps in the jupyter-notebook to create a dashboard and link
it to the log. Once you have the `dashboard_id` and it is connected to the
log, you will be able to upload source code either using the notebook, or using
the `upload.sh` script. For a detailed instruction of how to upload also
other types of dashboards, as well as a Bash and Python guide to upload, please
see `UploadTutorial.pdf`.

### Uploading/replacing source-code of an existing Advanced dashboard

First, stop tracking the `upload.sh` file by git on your local machine, so that
you can modify its content without making actual changes to the original version
in the repository, by accident.

`git update-index --assume-unchanged upload.sh`

After assigning value to the `API_KEY`, `DASHBOARD_ID` and `URL` variables
respectively in the `upload.sh` scipt, open a terminal and execute it:

`bash upload.sh`

Refresh the Advanced Dashboard page in LANA PM and the dashboard should appear.

## Add Graphs, Interactions and Pages
This repository follows a basic structure to allow for multiple pages. Each page has its own URL with navigation enabled by the navbar at the top. The folder structure is as follows:

The provided _dashboards_ project example follows a basic structure to allow for multiple pages. Each page
has its own URL with navigation enabled by the navigation-bar at the top. The folder
structure is as follows:

```
- app.py
Expand All @@ -42,10 +120,14 @@ This repository follows a basic structure to allow for multiple pages. Each page
|-- indicator_objects.py
|-- api_requests.py
```
Common functionality should be shared across pages using the `dashboard-components` submodule. Elements that have the same formatting should be defined as functions and not as copy pasted code. If API calls or data can be used by other functions, they should also be implemented in an accessible way. The more functions elements and functions are implemented, the fewer time it will take to build new dashboards.
On how to use submodules please see the Wiki entry here:
https://github.com/lanalabs/python-dashboard/wiki/Working-with-submodules-in-VS-Code



Common functionalities should be shared across pages using the
`dashboard-components` git-submodule. Elements that have the same formatting
should be defined as functions and not as copy pasted code. In case API calls or
data needs to be used by any other function, those should also be implemented in
an accessible way. The more function elements and functions are implemented,
the fewer time it will take to build new dashboards. On how to use submodules
please see the Wiki entry here:

https://github.com/lanalabs/python-dashboard/wiki/Working-with-submodules-in-VS-Code
.
59 changes: 59 additions & 0 deletions develop.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#!/usr/bin/env bash

#set -e

LOG_ID=""
API_KEY=""
DASHBOARD_NAME="csimple"
ASSETSRC="minimalistic_dashboard"
URL="http://localhost:4000"
ASSET="../upload.zip"

while [ 1 ]; do
rm -f $ASSET
pushd $ASSETSRC
zip -r $ASSET *
popd

DASHBOARD_ID=$(curl -X POST ${URL}/api/v2/custom-dashboards \
-H "Authorization: API-Key ${API_KEY}" \
-H "Content-Type: application/json" \
--data-raw '{
"name": "csimple",
"type": "python_dashboard"
}' | jq -r .id)

echo "Advanced dashboard id: ${DASHBOARD_ID}"

echo "Uploading ..."

curl -X POST ${URL}/api/v2/custom-dashboards/${DASHBOARD_ID}/source \
-H "Authorization: API-Key ${API_KEY}" \
-F file=@"./upload.zip"

echo "Connecting ..."

curl -X POST ${URL}/api/v2/resource-connections \
-H "Authorization: API-Key ${API_KEY}" \
-H "Content-Type: application/json" \
--data-raw '{
"log_id":"'"${LOG_ID}"'",
"custom_dashboard_id":"'"${DASHBOARD_ID}"'"
}' 1>/dev/null

echo
echo "Either press Ctrl-C to interrupt _or_ ENTER to clean up and move on!"

read

echo "DELETING dashboard ..."

curl -X DELETE ${URL}/api/v2/custom-dashboards/${DASHBOARD_ID} \
-H "Authorization: API-Key ${API_KEY}" \
-H "Content-Type: application/json"

echo "Press Enter to move on!"
read
done

exit 0
18 changes: 18 additions & 0 deletions minimalistic_dashboard/api_calls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import requests
import argparse
import ast

from dashboard_components.api_abstraction import aggregate

def number_of_cases(log_id, auth_token, tfs=[]) -> int:
"""Returns the number of cases."""

df = aggregate(auth_token,
trace_filter_sequence=tfs,
log_id=log_id,
metric="frequency",
aggregation_function="sum"
)
return int(df["frequency"].iloc[0])


30 changes: 30 additions & 0 deletions minimalistic_dashboard/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import dash
import os
import dash_bootstrap_components as dbc

from flask import Flask
from urllib.parse import urlparse

try:
fooConfig = urlparse(os.environ['JANUS_URL'])
config = {"scheme": fooConfig.scheme,
"host": fooConfig.hostname,
"port": fooConfig.port,
"url": fooConfig.url,
"dashboard_id": os.path.basename(os.getcwd())}
except Exception:
config = {"scheme": "http",
"host": "janus",
"port": 4000,
"url": "http://localhost",
"dashboard_id": os.path.basename(os.getcwd())}

application = Flask("example_dashboard")

app = dash.Dash(name=__name__,
server=application,
suppress_callback_exceptions=True,
external_stylesheets=[dbc.themes.FLATLY],
url_base_pathname=f'/{config["dashboard_id"]}/')

server = app.server
42 changes: 42 additions & 0 deletions minimalistic_dashboard/apps/page_1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import lana_listener
import dash_html_components as html
import dash_bootstrap_components as dbc

from app import app
from api_calls import number_of_cases
from dash.dependencies import Input, Output
from dashboard_components.indicator_objects import indicator_div

llistener = lana_listener.LanaListener(
id='LanaListener',
lana_api_key='',
lana_log_id='',
lana_trace_filter_sequence='[]'
)

layout = html.Div(children=[llistener,
html.Br(),
dbc.Row([dbc.Col(id="number_cases", width={"size": 3}),
dbc.Col(id="number_cases_tfs", width={"size": 3})
])])

@app.callback(
Output(component_id='number_cases', component_property='children'),
[Input('LanaListener', 'lana_api_key'),
Input('LanaListener', 'lana_log_id')]
)
def number_cases(api_key, log_id):
num_cases = number_of_cases(log_id, api_key)
ind = indicator_div(num_cases, title="Anzahl Cases (ohne TFS)")
return ind

@app.callback(
Output(component_id='number_cases_tfs', component_property='children'),
[Input('LanaListener', 'lana_api_key'),
Input('LanaListener', 'lana_log_id'),
Input('LanaListener', 'lana_trace_filter_sequence')]
)
def number_case_tfs(api_key, log_id, tfs):
num_cases = number_of_cases(log_id, api_key, tfs)
ind = indicator_div(num_cases, title="Anzahl Cases (mit TFS)")
return ind
1 change: 1 addition & 0 deletions minimalistic_dashboard/dashboard_components
Submodule dashboard_components added at 9ca27b
26 changes: 26 additions & 0 deletions minimalistic_dashboard/index.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import dash_core_components as dcc
import dash_html_components as html

from app import app, application, config
from apps import page_1
from dash.dependencies import Input, Output

print(f"Server {str(application)} running.")

app.layout = html.Div([
dcc.Location(id='url', refresh=True),
html.Div(id='page-content')
])

@app.callback(Output('page-content', 'children'),
[Input('url', 'pathname')])
def display_page(pathname):
dashboard_id = config["dashboard_id"]
if pathname == f'/{dashboard_id}/apps/app1':
return page_1.layout
else:
return page_1.layout


if __name__ == '__main__':
app.run_server(debug=False)
5 changes: 3 additions & 2 deletions upload.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/bin/bash
#!/usr/bin/env bash

API_KEY="<API_KEY>"
DASHBOARD_ID="<DASHBOARD_ID>"
URL="<URL>"
Expand All @@ -8,4 +9,4 @@ cd dashboard; zip -r ../upload.zip *; cd ..

curl -X POST "${URL}/api/v2/custom-dashboards/${DASHBOARD_ID}/source" \
-H "Authorization: API-Key ${API_KEY}" \
-F file=@"upload.zip"
-F file=@"upload.zip"