Skip to content
This repository was archived by the owner on Jan 14, 2025. It is now read-only.

Commit 9c469a1

Browse files
committed
refactor code + add legend
1 parent b50f24b commit 9c469a1

File tree

7 files changed

+264
-144
lines changed

7 files changed

+264
-144
lines changed

frontend/src/colors.js

Lines changed: 0 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -8,80 +8,4 @@ export function violationScale(min,max){
88
export function frequencyScale(min, max){
99
return d3.scaleSequential().domain([min,max])
1010
.interpolator(d3.interpolatePuBu)
11-
}
12-
13-
//updated from: https://observablehq.com/@d3/color-legend
14-
export function colorLegend({colorScale, title} = {}) {
15-
const tickSize = 6
16-
const width = 320
17-
const height = 44 + tickSize
18-
19-
d3.select("#legend")
20-
.selectAll("*")
21-
.remove();
22-
23-
d3.select('#legend').append('svg').attr('id', 'svg-legend')
24-
const appendedSvg = d3.select('#svg-legend')
25-
.attr("id", "svg-legend")
26-
.attr("width", width)
27-
.attr("height", height)
28-
.attr("viewBox", [0, 0, width, height])
29-
.style("overflow", "visible")
30-
.style("display", "block")
31-
32-
const marginTop = 18
33-
const marginRight = 0
34-
const marginBottom = 16 + tickSize
35-
const marginLeft = 0
36-
const ticks = width / 64
37-
38-
let tickAdjust = g => g.selectAll(".tick line").attr("y1", marginTop + marginBottom - height);
39-
let x = Object.assign(colorScale.copy()
40-
.interpolator(d3.interpolateRound(marginLeft, width - marginRight)), {
41-
range() {
42-
return [marginLeft, width - marginRight];
43-
}
44-
});
45-
46-
appendedSvg.append("image")
47-
.attr("x", marginLeft)
48-
.attr("y", marginTop)
49-
.attr("width", width - marginLeft - marginRight)
50-
.attr("height", height - marginTop - marginBottom)
51-
.attr("preserveAspectRatio", "none")
52-
.attr("xlink:href", ramp(colorScale.interpolator()).toDataURL());
53-
54-
//compute tick values
55-
const n = Math.round(ticks + 1);
56-
const tickValues = d3.range(n).map(i => d3.quantile(colorScale.domain(), i / (n - 1)));
57-
58-
appendedSvg.append("g")
59-
.attr("transform", `translate(0,${height - marginBottom})`)
60-
.call(d3.axisBottom(x)
61-
.ticks(ticks)
62-
.tickSize(tickSize)
63-
.tickValues(tickValues))
64-
.call(tickAdjust)
65-
.call(g => g.select(".domain").remove())
66-
.call(g => g.append("text")
67-
.attr("x", marginLeft)
68-
.attr("y", marginTop + marginBottom - height - 6)
69-
.attr("fill", "currentColor")
70-
.attr("text-anchor", "start")
71-
.attr("font-weight", "bold")
72-
.text(title));
73-
74-
return appendedSvg.node();
75-
}
76-
77-
function ramp(color, n = 256) {
78-
var canvas = document.createElement('canvas');
79-
canvas.width = n;
80-
canvas.height = 1;
81-
const context = canvas.getContext("2d");
82-
for (let i = 0; i < n; ++i) {
83-
context.fillStyle = color(i / (n - 1));
84-
context.fillRect(i, 0, 1, 1);
85-
}
86-
return canvas;
8711
}

frontend/src/conformance.js

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,19 @@
11
import globals from './globals.js';
22
import { mxgraph } from './mxgraph-initializer';
3-
import { violationScale, colorLegend } from './colors.js'
3+
import { violationScale } from './colors.js'
4+
import { colorLegend, overlayLegend } from './legend.js';
45
import { getDeviationOverlay, getSynchronousOverlay } from './overlays.js'
56
import { getBpmnActivityElementbyName } from './utils.js';
67
import { ShapeBpmnElementKind } from 'bpmn-visualization';
78

89
export function getAlignment(formData) {
910
console.log("Get alignments...");
10-
try{
11-
fetch('http://localhost:6969/conformance/alignment',{
11+
return fetch('http://localhost:6969/conformance/alignment',{
1212
method: 'POST',
1313
body: formData
1414
}).then(response => response.json())
1515
.then(data => visualizeAlignment(data))
16-
}
17-
catch(error){
18-
console.log(error)
19-
}
16+
.catch(error => console.log(error))
2017
}
2118

2219
function visualizeAlignment(alignedTraces){
@@ -34,7 +31,8 @@ function visualizeAlignment(alignedTraces){
3431
let activityCurrentStyle = null
3532
let activityCell = null
3633

37-
//first reset fill anf font color
34+
//first reset fill and font color
35+
// and remove overlays if existing
3836
let activities = globals.bpmnVisualization.bpmnElementsRegistry.getElementsByKinds(ShapeBpmnElementKind.TASK)
3937
let activityCells = activities.map(elt => mxGraph.getModel().getCell(elt.bpmnSemantic.id))
4038
mxGraph.getModel().beginUpdate()
@@ -45,40 +43,53 @@ function visualizeAlignment(alignedTraces){
4543
mxGraph.getModel().endUpdate();
4644
}
4745

46+
//remove overlays
47+
activities.forEach(act => globals.bpmnVisualization.bpmnElementsRegistry.removeAllOverlays(act.bpmnSemantic.id))
48+
49+
4850
//set violation color
49-
for (const [activityName, violationValue] of Object.entries(stats.normalizedStats)) {
51+
for (const [activityName, violationRatio] of Object.entries(stats.normalizedStats)) {
5052
const activityElement = getBpmnActivityElementbyName(activityName)
5153
if(activityElement){
5254
activityCell = mxGraph.getModel().getCell(activityElement.bpmnSemantic.id)
5355
activityCurrentStyle = mxGraph.getModel().getStyle(activityCell)
5456
mxGraph.getModel().beginUpdate()
5557
try {
56-
let style = mxgraph.mxUtils.setStyle(activityCurrentStyle, "fillColor", myViolationScale(violationValue*100))
58+
let style = mxgraph.mxUtils.setStyle(activityCurrentStyle, "fillColor", myViolationScale(violationRatio*100))
5759
mxGraph.getModel().setStyle(activityCell, style);
60+
activityCurrentStyle = mxGraph.getModel().getStyle(activityCell)
5861
//different way of setting the style
5962
//mxGraph.setCellStyles("fillColor", "red", [activityCell]);
6063

6164
//set label to white when the activity fillColor is above the scale average
62-
if(violationValue > 0.5){
63-
activityCurrentStyle = mxGraph.getModel().getStyle(activityCell)
65+
if(violationRatio > 0.5){
6466
style = mxgraph.mxUtils.setStyle(activityCurrentStyle, 'fontColor', 'white')
6567
mxGraph.getModel().setStyle(activityCell, style);
6668
}
6769
} finally {
6870
mxGraph.getModel().endUpdate();
6971
}
72+
//add overlay
73+
globals.bpmnVisualization.bpmnElementsRegistry.addOverlays(
74+
activityElement.bpmnSemantic.id,
75+
[
76+
getDeviationOverlay(stats.aggStats[activityName].modelMove,
77+
violationRatio,
78+
myViolationScale(violationRatio*100)),
79+
getSynchronousOverlay(stats.aggStats[activityName].syncMove)
80+
])
7081
}
71-
//add overlay
72-
globals.bpmnVisualization.bpmnElementsRegistry.addOverlays(
73-
activityElement.bpmnSemantic.id,
74-
[getDeviationOverlay(stats.aggStats[activityName].modelMove, violationValue, myViolationScale(violationValue*100)),
75-
getSynchronousOverlay(stats.aggStats[activityName].syncMove)])
82+
7683
}
7784
//add legend
7885
colorLegend({
7986
colorScale: myViolationScale,
80-
title: "% of deviations (model moves)"
87+
title: "% deviations (model moves)"
8188
})
89+
90+
overlayLegend({
91+
leftOverlayLegend: "# conformoties\n(synchronous moves)",
92+
rightOverlayLegend : "# deviations\n(model moves)"})
8293
}
8394

8495
/**

frontend/src/conversion.js

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
11
export function convertToCSV(formData){
2-
try{
3-
fetch('http://localhost:6969/conversion/xes-to-csv', {
4-
method: 'POST',
5-
body: formData
6-
}).then(response => response.json())
7-
.then(data => visualizeCSV(data))
8-
9-
}
10-
catch(error){
11-
console.log(error)
12-
}
2+
fetch('http://localhost:6969/conversion/xes-to-csv', {
3+
method: 'POST',
4+
body: formData
5+
}).then(response => response.json())
6+
.then(data => visualizeCSV(data))
7+
.catch(error => console.log(error))
138
}
149

1510
function visualizeCSV(data){

frontend/src/discovery.js

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,19 @@ import globals from './globals.js';
22
import { mxgraph } from './mxgraph-initializer';
33

44
import { FitType, ShapeBpmnElementKind } from 'bpmn-visualization';
5-
import { frequencyScale, colorLegend } from './colors.js'
5+
import { frequencyScale } from './colors.js'
6+
import { getFrequencyOverlay } from './overlays.js';
7+
import { colorLegend, overlayLegend } from './legend.js';
68
import { getBpmnActivityElementbyName } from './utils.js';
79

810
export function getBPMNDiagram(formData) {
911
console.log('Get bpmn...');
10-
try{
11-
fetch('http://localhost:6969/discover/inductive-miner', {
12+
return fetch('http://localhost:6969/discover/inductive-miner', {
1213
method: 'POST',
1314
body: formData
1415
}).then(response => response.text())
1516
.then(data => visualizeBPMN(data))
16-
17-
}
18-
catch(error){
19-
console.log(error)
20-
}
17+
.catch(error => console.log(error))
2118
}
2219

2320
function visualizeBPMN(data) {
@@ -34,14 +31,10 @@ function visualizeBPMN(data) {
3431

3532
function computeFrequency(){
3633
console.log('Compute frequency stats...');
37-
try{
38-
fetch('http://localhost:6969/stats/frequency')
34+
fetch('http://localhost:6969/stats/frequency')
3935
.then(response => response.json())
4036
.then(data => visualizeFrequency(data))
41-
}
42-
catch(error){
43-
console.log(error)
44-
}
37+
.catch(error => console.log(error))
4538
}
4639

4740
function visualizeFrequency(data) {
@@ -66,17 +59,21 @@ function visualizeFrequency(data) {
6659
const activityElement = getBpmnActivityElementbyName(activityName)
6760
if(activityElement){
6861
activityCell = mxGraph.getModel().getCell(activityElement.bpmnSemantic.id)
62+
//activityCurrentStyle = mxGraph.getCurrentCellStyle(activityCell)
6963
activityCurrentStyle = mxGraph.getModel().getStyle(activityCell)
64+
7065
mxGraph.getModel().beginUpdate()
7166
try {
7267
let style = mxgraph.mxUtils.setStyle(activityCurrentStyle, 'fillColor', myFrequencyScale(freqValue))
7368
mxGraph.getModel().setStyle(activityCell, style);
74-
//different way of setting the style
75-
//mxGraph.setCellStyles("fillColor", myFrequencyScale(freqValue), [activityCell]);
69+
activityCurrentStyle = mxGraph.getModel().getStyle(activityCell)
70+
//different ways of setting the style
71+
//mxGraph.setCellStyles("fillColor", myFrequencyScale(freqValue), [activityCell]);
72+
//or
73+
//mxGraph.setCellStyles(mxgraph.mxConstants.STYLE_FILLCOLOR, 'red', [activityCell]);
7674

7775
//set label to white when the activity fillColor is above the scale average
7876
if (freqValue > avg){
79-
activityCurrentStyle = mxGraph.getModel().getStyle(activityCell)
8077
style = mxgraph.mxUtils.setStyle(activityCurrentStyle, 'fontColor', 'white')
8178
mxGraph.getModel().setStyle(activityCell, style);
8279
//different way of setting the style
@@ -85,12 +82,20 @@ function visualizeFrequency(data) {
8582
} finally {
8683
mxGraph.getModel().endUpdate();
8784
}
88-
}
85+
86+
//add frequency overlay
87+
globals.bpmnVisualization.bpmnElementsRegistry.addOverlays(
88+
activityElement.bpmnSemantic.id,
89+
getFrequencyOverlay(freqValue, max,
90+
myFrequencyScale(freqValue)))
91+
}
8992
}
9093

9194
//add legend
9295
colorLegend({
9396
colorScale: myFrequencyScale,
9497
title: "Frequency of execution"
95-
})
98+
})
99+
100+
overlayLegend({rightOverlayLegend : "# executions"})
96101
}

frontend/src/index.js

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import { getAlignment } from './conformance.js';
33
import { convertToCSV } from './conversion.js';
44
import './styles.css';
55

6-
const fileUpload = document.getElementById("xes-file-discovery")
7-
fileUpload.addEventListener("change", function(e){
6+
const fileUpload = document.getElementById('xes-file-discovery')
7+
fileUpload.addEventListener('change', function(e){
88
document.getElementById('show-xes-log').classList.remove('d-invisible');
99
})
1010

@@ -17,30 +17,34 @@ document.getElementsByClassName('modal-overlay').item(0).onclick = () => {
1717
modalElt.classList.remove('active');
1818
};
1919

20-
const showEventLogButton = document.getElementById("show-xes-log")
21-
showEventLogButton.addEventListener("click", function(e){
22-
let file = fileUpload.files[0]
20+
const showEventLogButton = document.getElementById('show-xes-log')
21+
showEventLogButton.addEventListener('click', function(e){
22+
let file = document.getElementById('xes-file-discovery').files[0]
2323
let formData = new FormData()
2424
formData.append('file', file)
2525
convertToCSV(formData)
2626
modalElt.classList.add('active');
2727
})
2828

29-
const discoverBpmnButton = document.getElementById("discover-bpmn")
30-
discoverBpmnButton.addEventListener("click", function(e){
31-
let file = fileUpload.files[0]
32-
let noiseThreshold = document.getElementById("noise-threshold").value
29+
const discoverBpmnButton = document.getElementById('discover-bpmn')
30+
discoverBpmnButton.addEventListener('click', function(e){
31+
discoverBpmnButton.classList.add('loading')
32+
let file = document.getElementById('xes-file-discovery').files[0]
33+
let noiseThreshold = document.getElementById('noise-threshold').value
3334
let formData = new FormData()
3435
formData.append('file', file)
3536
formData.append('noise', noiseThreshold)
36-
getBPMNDiagram(formData)
37+
getBPMNDiagram(formData).then(() => discoverBpmnButton.classList.remove('loading'))
38+
39+
3740
})
3841

3942

40-
const conformanceButton = document.getElementById("compute-conformance")
43+
const conformanceButton = document.getElementById('compute-conformance')
4144
conformanceButton.addEventListener('click', function(e){
42-
let file = document.getElementById("xes-file-conformance").files[0]
45+
conformanceButton.classList.add('loading')
46+
let file = document.getElementById('xes-file-conformance').files[0]
4347
let formData = new FormData()
4448
formData.append('file', file)
45-
getAlignment(formData)
49+
getAlignment(formData).then(() => conformanceButton.classList.remove('loading'))
4650
})

0 commit comments

Comments
 (0)