Welcome to the Process Analytics series of tutorials 👋
The Process Analytics project consists of open source libraries and components that allow you to get a visual overview of your business process and the process execution data.
In this introductory tutorial, you will learn how to use bpmn-visualization, a TypeScript library for visualizing process data on top of process models expressed in BPMN which is the de facto standard for process modeling.
⚠️ This tutorial assumes that you have a basic understanding of HTML, CSS and JavaScript. We provide a ready to use bootstrap template so that you can directly start the development 🚀 without getting your hands dirty with the project configuration 🤕. There will be a lot of examples, so go ahead, and initialize your project 👇
Initialize your project
To follow and reproduce the tutorial steps in your own environment, we created a preconfigured project template that you can open with your preferred local or online IDE (e.g. gitpod, CodeSandobx, etc.). Follow the steps below to set up and initialize the project template.
Step 1: git clone the repository or download the zip and open it in your favorite IDE.
Step 2: Open a terminal window in the project location and run the commands below.
If you’re using an online editor like gitpod, these two commands may be automatically launched, and you should directly see the message shown in the figure below.
If you’re using a local editor, you must install Node.js and the npm command line interface. You can check the details here. This tutorial has been tested with Node 16 and npm 8.16.0.
npm install
npm start
npm install
installs the required packages while npm start
starts a dev server that will continuously update the page while you are updating the code. We use Vite JS which is a front-end development platform for modern web projects.
If the commands are successfully executed, you should see the message below which states that Vite JS has launched a dev server available at http://localhost:5173/. Click on the link to see the code output. The page will be automatically updated whenever you make changes to the code.
Let’s take a quick look at the main project structure. The src package contains two files: index.js in which we will add the JavaScript code related to the usage of bpmn-visualization and styles.css in which we will add CSS classes to apply custom styles. The index.html contains the HTML code that will be kept unchanged in this tutorial.
Before diving into the technical details, we will briefly explain what the bpmn-visualization library can do for you, then we will show you how to use it in an online monitoring scenario. Let’s go 🎬
What can bpmn-visualization do for you?
bpmn-visualization is a TypeScript library for visualizing process execution data on BPMN diagrams. The version 0.27.0 provides the following features:
- BPMN graph rendering and fitting options;
-
Display options for execution data which include:
- adding overlays 📛 to the process model elements (e.g. for displaying statistics computed from process execution data)
- customizing the style 🖋️ of the process model elements including colors, fonts, sizes and icons (e.g. for highlighting performance issues, errors and warning)
- Interactive capacities with the process model elements (e.g. adding tooltips on mouse hover/click)
The general architecture revolves around three key components: BPMN parser, BPMN renderer and interactions.
The BPMN parser loads XML BPMN files, and converts them into an internal model. The internal BPMN model is then passed to the BPMN renderer component, which is in charge of rendering the BPMN elements. The interactions component empowers developers and end users with flexibility in interacting with the BPMN elements. For more information, check out the bpmn-visualization user documentation.
Use case of this tutorial: an online monitoring scenario ⏲️
You will learn how to use bpmn-visualization by taking a concrete process analytics scenario which is_ online monitoring_. In online monitoring, an administrator monitors the execution of the process at run-time. Using bpmn-visualization, we will load a BPMN process and display two important pieces of information:
- The running instances and their number ⚙️.
- The state of running instances: whether or not they are violating the pre-defined KPIs ⏱️.
Let’s see now how it works in practice 👩💻
How to use bpmn-visualization?
If you are stuck at any step in the tutorial, you can find the complete solution here.
The user of bpmn-visualization is responsible for retrieving the BPMN diagram which could be a local or a remote file fetched via an API. We will use a BPMN diagram available from bpmn-miwg-test-suite which provides a collection of BPMN test cases created by the OMG BPMN Model Interchange Working Group (BPMN MIWG) to test the interoperability between different BPMN tools.
Create a diagram.bpmn file in your src directory and copy the content of the C.1.0 BPMN file.
Initialize bpmn-visualization and load the diagram
For the remainder of this part, make sure to check the bpmn-visualization API documentation.
The first step consists of importing and instantiating the BpmnVisualization
class. For the instantiation, we will pass as input the id of the HTML element where the BPMN diagram will be rendered (in our case, bpmn-container
is the id of the HTML element declared in index.html). Additionally, we can set the navigation
to true
to enable the diagram panning and zooming.
Edit the src/index.js file by removing the placeholder code and adding the following:
import { BpmnVisualization } from 'bpmn-visualization';
const bpmnVisualization = new BpmnVisualization({
container: 'bpmn-container',
navigation: { enabled: true }
});
Now that a bpmnVisualization
object is created and initialized, we can load the diagram by calling the load
method. The load
method takes as input the BPMN file content as a string
, which is in our case the content of the diagram.bpmn file we created previously. Additional parameters can be passed to specify the fitting options (using the enumeration FitType
imported from bpmn-visualization
) and whether the model needs to be filtered.
Let’s see how to center a diagram on load. First, update the import
line of the code above by adding the FitType
class. Then, import the content of the diagram.bpmn file as a string and store it in the variable diagram
as shown below.
import { BpmnVisualization, FitType } from 'bpmn-visualization';
// the '?raw' parameter tells Vite to store the diagram content in a string.
// for more details, see https://vitejs.dev/guide/assets.html#importing-asset-as-string
// for other load methods, see https://github.com/process-analytics/bpmn-visualization-examples
import diagram from './diagram.bpmn?raw';
To load the diagram, add the code snippet below right after the initialization of the BpmnVisualization
class. The diagram
variable is passed to the load
method along with the additional parameter fit
for which the type
property is set to FitType.Center
.
bpmnVisualization.load(diagram, {
fit: { type: FitType.Center }
});
Save the code and you’ll see the BPMN diagram of our process visualized as shown in the figure below. It consists of two pools, Team Assistant and Process Engine - Invoice Receipt.
Let’s simplify the model by focusing only on the Process Engine - Invoice Receipt pool. We will modify the load
method by adding the modelFilter
option. The modelFilter
option has the pools
property that allows to specify the pools to keep in the model. A pool can be referenced by its id
and/or its name
. In the code below, we indicate that the pool named Process Engine - Invoice Receipt is kept while the remaining pools are filtered out.
bpmnVisualization.load(diagram, {
fit: { type: FitType.Center },
modelFilter: {
pools: [
{
name: 'Process Engine - Invoice Receipt'
}
]
}
});
The result should be the filtered model below containing only the Process Engine - Invoice Receipt pool. Now that we have the process visualized 🥳, let’s breathe life into it 🌈
Visualize monitoring data
Add testing data
Let’s first add some testing data. For the purpose of this tutorial, we will mock real data with methods returning arbitrary data. We will define two functions: getActivitiesRunningInstances()
for returning information about running activities and getEdgesWaitingInstances()
for returning information about process instances waiting on specific edges.
The getActivitiesRunningInstances()
returns a Map
consisting of key-value pairs where the key
is the id of the BPMN activities (as defined in the BPMN file) and the value
is an object consisting of the number of on-time, risky and critically running instances.
Add the following code at the end of your src/index.js file.
function getActivitiesRunningInstances() {
return new Map([
['assignApprover', { onTime: 5, risky: 0, critical: 0 }],
['approveInvoice', { onTime: 2, risky: 3, critical: 0 }],
['reviewInvoice', { onTime: 4, risky: 1, critical: 2 }],
['prepareBankTransfer', { onTime: 0, risky: 0, critical: 0 }],
['archiveInvoice', { onTime: 0, risky: 0, critical: 0 }],
]);
}
Similarly, add the getEdgesWaitingInstances()
function below which returns a Map
consisting of key-value pairs where the key
is the id of the edges on which there are waiting instances.
function getEdgesWaitingInstances() {
return new Map([['invoiceApproved', 2]]);
}
Interact with BPMN elements of the process diagram
The interaction with the process diagram is done via BpmnElementsRegistry
which provides different methods to find and retrieve the BPMN elements of the diagram. In this tutorial, we will cover the addOverlays
and addCssClasses
.
Add overlays to show the number of running instances ⚙️
The addOverlays
method takes as input the id of the BPMN element and one or multiple Overlay
objects. An Overlay object is defined in terms of its label
, position
and style
.
The code below illustrates the usage of the method (don’t add it to your code). It adds an overlay to the activity Assign Approver whose id is assignApprover
(you can find the id of an element in the src/diagram.bpmn file). The overlay is labeled with “5”. It is positioned in the top-center
of the activity. Its style is defined in terms of the font
, fill
color and stroke
.
bpmnVisualization.bpmnElementsRegistry.addOverlays('assignApprover', {
position: 'top-center',
label: '5',
style: {
font: { color: 'white', size: 16 },
fill: { color: 'green', opacity: 50 },
stroke: { color: 'green', width: 2 }
}
});
Let’s now add overlays to activities using our testing data. Add the code below to your src/index.js file. The code retrieves and stores the activities’ running instances in the variable activitiesRunningInstances
. Then, it iterates over the elements in activitiesRunningInstances
using the foreach method. For each element, it adds three overlays: in the top-center
for instances running on time, in the top-left
for instances running late with a risky level and in the top-right
for instances running late with a critical level.
const activitiesRunningInstances = getActivitiesRunningInstances();
activitiesRunningInstances.forEach((value, activityId) => {
// running on time
if (value.onTime) {
bpmnVisualization.bpmnElementsRegistry.addOverlays(activityId, {
position: 'top-center',
label: `${value.onTime}`,
style: {
font: { color: 'white', size: 16 },
fill: { color: 'green', opacity: 50 },
stroke: { color: 'green', width: 2 },
},
});
}
// running late with risky level
if (value.risky) {
bpmnVisualization.bpmnElementsRegistry.addOverlays(activityId, {
position: 'top-left',
label: `${value.risky}`,
style: {
font: { color: 'white', size: 16 },
fill: { color: '#FF8C00', opacity: 50 },
stroke: { color: '#FF8C00', width: 2 },
},
});
}
// running late with critical level
if (value.critical) {
bpmnVisualization.bpmnElementsRegistry.addOverlays(activityId, {
position: 'top-right',
label: `${value.critical}`,
style: {
font: { color: 'white', size: 16 },
fill: { color: 'red', opacity: 50 },
stroke: { color: 'red', width: 2 },
},
});
}
});
In the same way, information about instances waiting on specific edges can be retrieved by calling the function getEdgesWaitingInstances
and storing the result in the map edgesWaitingInstances
. Then, overlays can be added to each edge by iterating over the elements in edgesWaitingInstances
as shown below:
const edgesWaitingInstances = getEdgesWaitingInstances();
edgesWaitingInstances.forEach((value, edgeId) => {
bpmnVisualization.bpmnElementsRegistry.addOverlays(edgeId, {
position: 'middle',
label: `${value}`,
style: {
font: { color: 'white', size: 16 },
fill: { color: 'red', opacity: 50 },
stroke: { color: 'red', width: 2 },
},
});
});
The figure below shows the resulting visualization.
Let’s now customize the style of elements 🎆 to highlight their state with respect to KPIs.
Add CSS classes to highlight the state of running instances ⏱️
Do you remember your first university course Introduction to Web Development? 🕸️ Well, it might be old 👴 (but gold 🙂). Before explaining how addCssClasses
works, let’s try to remember the syntax of a CSS class selector. We will define a new CSS class to fill all the process elements with a gray color. To correctly define CSS class selectors, a minimal knowledge of the HTML DOM tree is required. Let’s see what the HTML DOM tree looks like.
Check HTML DOM Structure
In your browser, perform a right click on any BPMN element, for example on the activity Assign Approver. Click on Inspect and check its DOM structure. As shown below, you can see that, in general, a BPMN activity with a specific type (e.g. a user activity as in our case) is defined as an SVG group consisting of two elements: a rectangle rect representing the shape of the activity and a path for drawing the activity icon (the user icon in our case).
Therefore, when defining a CSS class selector, one should explicitly define the element to which the CSS is applied.
Define CSS class selectors
Since we want to style all our BPMN elements (activities, gateways and events) by filling them with a gray color, it is recommended to reuse the default CSS classes instead of adding new ones. The classes allow identifying elements of the same family and of the same specific type. During BPMN diagram rendering, bpmn-visualization sets these CSS classes to all elements according to their types.
For example, in the above HTML DOM figure, we can see that the activity Assign Approver has the following classes: bpmn-type-activity, bpmn-type-task and bpmn-user-task. The first class indicates that the element is an activity, the second and third classes give more information about the type of the activity: it is a task and more specifically a user task (check the BPMN specification for more details about the different BPMN elements). Therefore, to apply a specific style to all activities (regardless of their type), we can reuse the default CSS class bpmn-type-activity.
Add the CSS code below to your src/styles.css file. This CSS class selector fills all events (more specifically their ellipse
element), activities (more specifically their rect
element) and gateways (more specifically their first path
element) with a gray color.
.bpmn-type-event > ellipse,
.bpmn-type-activity > rect,
.bpmn-type-gateway > path:nth-child(1) {
fill: rgb(230, 225, 225);
}
Your process model should now look like the figure below 👇.
Use the addCssClasses method
The addCssClasses
takes as input the id of one or several BPMN elements and one or more CSS class names to be applied. In this tutorial example, we will write CSS class selectors with some animations to:
- Highlight the state of running activities’ instances by:
- Adding a shadow color to activities:
- 🟢 Green: if all instances are running on time.
- 🔴 Red: if there exists at least one instance running critically late.
- 🟠 Orange : if there exists an instance running late with a risky level.
- Animating the stroke of activities with three different levels of speed following the same strategy as for the shadow colors.
- Adding a shadow color to activities:
- Highlight process instances waiting for a specific activity to be executed by:
- Coloring the input edge of the corresponding activity in 🔴 red.
- Animating the edge by adding a pulsating effect.
Add the CSS code below to your src/styles.css. The code shows the CSS class selector that will be added to the activities running on time. You don’t really have to be a CSS expert to define cool styles with animations. There are plenty of websites that generate ready to use CSS code for you. Just ask your super search engine 🦹♀️ for CSS generators. In our example, the filter
property allows defining a shadow. The stroke-dasharray
defines the pattern of dashes and gaps on the stroke. The remaining properties are defined for animating the stroke. Particularly, the animation-duration
allows us to set the speed of the animation.
.task-running-on-time > rect {
stroke-dasharray: 10, 5;
animation-name: dash-task;
animation-duration: 0.5s;
animation-timing-function: linear;
animation-iteration-count: infinite;
filter: drop-shadow(0 0 0.95em rgb(6, 146, 3));
}
@keyframes dash-task {
to {
stroke-dashoffset: -15;
}
}
The remaining CSS class selectors for tasks running risky (.task-running-risky
) and late (.task-running-critical
) should be straightforward:
- Change the color of the shadow in the
filter
property (set it orange 🟠 for.task-running-risky
and red 🔴 for.task-running-critical
) , and - Increase the
animation-duration
. For example, you can set it to1s
in.task-running-risky
and to2s
in.task-running-critical
.
Super easy, right? 🎃
Finally, edit your src/index.js by adding the CSS classes to the activities using the method addCssClasses
as shown in the code below:
activitiesRunningInstances.forEach((value, activityId) => {
if (value.critical) {
bpmnVisualization.bpmnElementsRegistry.addCssClasses(activityId, 'task-running-critical');
} else if (value.risky) {
bpmnVisualization.bpmnElementsRegistry.addCssClasses(activityId, 'task-running-risky');
} else if (value.onTime) {
bpmnVisualization.bpmnElementsRegistry.addCssClasses(activityId, 'task-running-on-time');
}
});
If you see the first signs of life in your process, then Congratulations 🎊 The result should be similar to the figure below.
Now could you guess what are the CSS class selectors for animating the edge and getting the final result as shown above in the cover photo of this tutorial? 🧐 Don’t forget to check the HTML DOM structure of edges!
Summary
In this tutorial, we learned how to use the bpmn-visualization library using an online monitoring example. We saw that the user of bpmn-visualization is responsible for retrieving the process diagram. Using the library, we loaded a BPMN diagram. Then, we filtered and enriched the process with overlays and CSS classes. The bpmn-visualization API offers a wide range of functionalities that can be explored here.
Going further
In a process analytics scenario, a process model may not be available. It is rather discovered from the execution data recorded by a process-aware information system using process discovery techniques. In a separate tutorial, we will talk about automatically generating BPMN diagrams from process execution data using the Model Generation Application provided also by the Process Analytics project.
That’s all for now! We’ll be glad to receive your feedback and answer your questions, just leave your comment below 👇
Stay tuned for more upcoming tutorials 📚
In the meantime, if you want to stay on top of the latest news and releases from the Process Analytics project, follow us through:
- Website: https://process-analytics.dev
- Twitter: @ProcessAnalyti1
- GitHub: https://github.com/process-analytics
- Discord: Join our server!
Acknowledgements: we warmly thank Adrien Kantcheff and Emmanuel Duchastenier for having tried the tutorial and reviewed the article.
Photo by Hans-Peter Gauster on Unsplash
Top comments (4)
great content!
small tip: you can convert the github.com/process-analytics/bpmn-... repo to a Github template
Yes, thanks for the tip. I have just activated the "template" support 👍
Hey thanks for the presentation. I would like to know how can I extract my event logs? In order to get in touch with process mining please.
Generating the event logs is out of scope of the Process Analytics project, especially when it first involves extracting data from a specific vendor (Bonita in your case). You will find more information and answers in the Bonita Community Q&A. See also Bonitasoft-Community/bonita-server-logs#2 (comment)