Skip to content
Snippets Groups Projects
Commit 687d4cc7 authored by Jonas Blatt's avatar Jonas Blatt :ant:
Browse files

Merge branch 'develop' into 'documentation/verifier'

Develop

See merge request jonasblatt/ma-jonasblatt-dmn-verifier!79
parents c15b0ce0 9d0a3346
No related branches found
No related tags found
No related merge requests found
Showing
with 337 additions and 62 deletions
......@@ -218,6 +218,20 @@ public class VDmnFunctions {
return clusters;
}
/**
* Get the name of a decision as string. If a name is present, the string of the name is returned.
* If the name is not present, the id of the decision is returned.
*
* @param decision the {@link VDmnDecision}
* @return a string, representing the "name" of the decision
*/
public static String getDecisionStringName(VDmnDecision decision) {
Validate.notNull(decision);
return decision.getName().isPresent()
? decision.getName().get().getValue()
: decision.getDecisionId().getValue();
}
/**
* Get the name of a column as string. If a label is present, the string of the label is returned.
* If the label is not present, the name is returned. If the name is not present, "[no name]" is
......@@ -229,8 +243,8 @@ public class VDmnFunctions {
public static String getColumnStringName(VDmnColumn column) {
Validate.notNull(column);
return column.getLabel().isPresent()
? column.getLabel().get().toString()
: column.getName().orElse(Name.NO_NAME).toString();
? column.getLabel().get().getValue()
: column.getName().orElse(Name.NO_NAME).getValue();
}
/**
......@@ -253,7 +267,7 @@ public class VDmnFunctions {
* @return a string "Decision %s: '
*/
public static String templateDecision(VDmnDecision decision) {
return String.format("Decision \"%s\": ", decision.getName().orElse(Name.NO_NAME));
return String.format("Decision \"%s\": ", getDecisionStringName(decision));
}
/**
......@@ -265,7 +279,7 @@ public class VDmnFunctions {
public static String templateDecisionColumn(VDmnColumn column) {
return String.format(
"Decision \"%s\", Column \"%s\": ",
column.getVDmnDecision().getName().orElse(Name.NO_NAME), getColumnStringName(column));
getDecisionStringName(column.getVDmnDecision()), getColumnStringName(column));
}
/**
......@@ -277,6 +291,6 @@ public class VDmnFunctions {
public static String templateDecisionRule(VDmnRule rule) {
return String.format(
"Decision \"%s\", Rule \"%s\": ",
rule.getVDmnDecision().getName().orElse(Name.NO_NAME), rule.getRowNumber());
getDecisionStringName(rule.getVDmnDecision()), rule.getRowNumber());
}
}
package de.unikoblenz.fgbks.core.dmn.verification.verifier.impl;
import static de.unikoblenz.fgbks.core.dmn.domain.vdmn.utils.VDmnFunctions.getDecisionStringName;
import static de.unikoblenz.fgbks.core.dmn.verification.result.actions.VerificationFix.SHOW_INPUT_DATA;
import de.unikoblenz.fgbks.base.domain.Name;
import de.unikoblenz.fgbks.core.dmn.domain.vdmn.VDmnDecision;
import de.unikoblenz.fgbks.core.dmn.domain.vdmn.VDmnInputColumn;
import de.unikoblenz.fgbks.core.dmn.domain.vdmn.VDmnInputData;
import de.unikoblenz.fgbks.core.dmn.verification.result.VerificationResultEntry.VerificationClassification;
import de.unikoblenz.fgbks.core.dmn.verification.result.VerificationResultEntryElement;
import de.unikoblenz.fgbks.core.dmn.verification.result.actions.Action;
import de.unikoblenz.fgbks.core.dmn.verification.result.actions.ActionScope;
import de.unikoblenz.fgbks.core.dmn.verification.result.actions.ActionType;
import de.unikoblenz.fgbks.core.dmn.verification.result.actions.VerificationFix;
import de.unikoblenz.fgbks.core.dmn.verification.verifier.AbstractVerifier;
import de.unikoblenz.fgbks.core.dmn.verification.verifier.DmnVerifier;
import de.unikoblenz.fgbks.core.dmn.verification.verifier.types.LonelyDataInputVerification;
......@@ -19,13 +26,53 @@ public class LonelyDataInputVerifier extends AbstractVerifier {
protected void doVerification() {
for (VDmnInputData i : dmnObjectContainer.getVDmnDefinition().getVDmnInputData()) {
if (i.getInformationProvidingDecisions().isEmpty()) {
// add element to result
vreFactory
.addElement(VerificationResultEntryElement.create(i))
.addVerificationFix(SHOW_INPUT_DATA)
.addToEntry(
VerificationClassification.WARNING,
"Input data \"%s\" has no outgoing connections to at least one decision node.",
i.getName().orElse(defaultName));
.addVerificationFix(SHOW_INPUT_DATA);
// add delete action
vreFactory.addVerificationFix(
VerificationFix.getBuilder()
.withFixName(new Name("Remove"))
.addAction(
Action.getBuilder()
.withActionScope(ActionScope.INPUT_DATA)
.withActionType(ActionType.DELETE)
.addIdValue(i.getInputDataId())
.build())
.build());
// search possible connections
searchPossibleDecisions(i);
// add to result
vreFactory.addToEntry(
VerificationClassification.WARNING,
"Input data \"%s\" has no outgoing connections to at least one decision node.",
i.getName().orElse(defaultName));
}
}
}
private void searchPossibleDecisions(VDmnInputData inputData) {
if (inputData.getName().isPresent()) {
for (VDmnDecision decision : dmnObjectContainer.getVDmnDefinition().getVDmnDecisions()) {
for (VDmnInputColumn inputColumn : decision.getVDmnDecisionTable().getVDmnInputColumns()) {
if (inputColumn.getName().isPresent()
&& inputColumn.getName().get().equals(inputData.getName().get())) {
// Add verification fix to add this input Data to the decision
vreFactory.addVerificationFix(
VerificationFix.getBuilder()
.withFixName(new Name("Connect to " + getDecisionStringName(decision)))
.addAction(
Action.getBuilder()
.withActionScope(ActionScope.INPUT_DATA)
.withActionType(ActionType.UPDATE)
.addIdValue(inputColumn.getVDmnDecision().getDecisionId())
.addIdValue(inputData.getInputDataId())
.addValue("name", inputColumn.getName().get().getValue())
.build())
.build());
}
}
}
}
}
......
package de.unikoblenz.fgbks.core.dmn.verification.verifier.impl;
import static de.unikoblenz.fgbks.core.dmn.domain.vdmn.utils.VDmnFunctions.getDecisionStringName;
import static de.unikoblenz.fgbks.core.dmn.verification.result.actions.Action.ACTION_SHOW_DECISION;
import static de.unikoblenz.fgbks.core.dmn.verification.result.actions.Action.ACTION_SHOW_INPUT_DATA;
import static de.unikoblenz.fgbks.core.dmn.verification.result.actions.VerificationFix.DEFAULT_SHOW_NAME;
......@@ -124,7 +125,7 @@ public class MissingInputColumnVerifier extends AbstractVerifier {
(inputDataNode.isInputData() ? "Input data" : "Decision")
+ " node \"%s\" has no corresponding input column in decision \"%s\"",
inputDataNode.getName().orElse(Name.NO_NAME),
decision.getName().orElse(Name.NO_NAME));
getDecisionStringName(decision));
}
}
}
package de.unikoblenz.fgbks.core.dmn.verification.verifier.impl;
import static de.unikoblenz.fgbks.core.dmn.domain.vdmn.utils.VDmnFunctions.getColumnStringName;
import static de.unikoblenz.fgbks.core.dmn.domain.vdmn.utils.VDmnFunctions.getDecisionStringName;
import static de.unikoblenz.fgbks.core.dmn.domain.vdmn.utils.VDmnFunctions.templateDecision;
import static de.unikoblenz.fgbks.core.dmn.verification.result.actions.VerificationFix.SHOW_INPUT_COLUMNS;
import de.unikoblenz.fgbks.base.domain.Name;
import de.unikoblenz.fgbks.core.dmn.domain.vdmn.VDmnDecision;
import de.unikoblenz.fgbks.core.dmn.domain.vdmn.VDmnInputColumn;
import de.unikoblenz.fgbks.core.dmn.domain.vdmn.VDmnInputData;
import de.unikoblenz.fgbks.core.dmn.domain.vdmn.VDmnNode;
import de.unikoblenz.fgbks.core.dmn.domain.vdmn.VDmnOutputColumn;
import de.unikoblenz.fgbks.core.dmn.verification.result.VerificationResultEntry.VerificationClassification;
......@@ -19,6 +21,7 @@ import de.unikoblenz.fgbks.core.dmn.verification.verifier.AbstractVerifier;
import de.unikoblenz.fgbks.core.dmn.verification.verifier.DmnVerifier;
import de.unikoblenz.fgbks.core.dmn.verification.verifier.types.MissingInputDataVerification;
import java.util.List;
import java.util.stream.Collectors;
@DmnVerifier(verifierType = MissingInputDataVerification.class)
public class MissingInputDataVerifier extends AbstractVerifier {
......@@ -66,34 +69,92 @@ public class MissingInputDataVerifier extends AbstractVerifier {
.addVerificationFix(SHOW_INPUT_COLUMNS);
// Action for creating input Data
if (inputColumn.getName().isPresent()) {
vreFactory.addVerificationFix(
VerificationFix.getBuilder()
.withFixName(new Name("Add Data Input"))
.addAction(
Action.getBuilder()
.addIdValue(inputColumn.getVDmnDecision().getDecisionId())
.withActionType(ActionType.CREATE)
.withActionScope(ActionScope.INPUT_DATA)
.addValue("name", inputColumn.getName().get().getValue())
.build())
.build());
vreFactory
.addVerificationFix(
VerificationFix.getBuilder()
.withFixName(new Name("Add new Data Input"))
.addAction(
Action.getBuilder()
.withActionType(ActionType.CREATE)
.withActionScope(ActionScope.INPUT_DATA)
.addIdValue(inputColumn.getVDmnDecision().getDecisionId())
.addValue("name", inputColumn.getName().get().getValue())
.build())
.build())
// Action for deleting column
.addVerificationFix(
VerificationFix.getBuilder()
.withFixName(new Name("Delete Column"))
.addAction(
Action.getBuilder()
.withActionType(ActionType.DELETE)
.withActionScope(ActionScope.INPUT_COLUMN)
.addIdValue(inputColumn.getVDmnDecision().getDecisionId())
.addIdValue(inputColumn.getInputId())
.build())
.build());
// Actions for connecting to existing input Data
searchPossibleInputData(inputColumn);
// Actions for connection to existing decisions
searchPossibleInputDecissions(inputColumn);
}
// Action for deleting column
vreFactory.addVerificationFix(
VerificationFix.getBuilder()
.withFixName(new Name("Delete Column"))
.addAction(
Action.getBuilder()
.addIdValue(inputColumn.getVDmnDecision().getDecisionId())
.addIdValue(inputColumn.getInputId())
.withActionType(ActionType.DELETE)
.withActionScope(ActionScope.INPUT_COLUMN)
.build())
.build());
// Add to result
vreFactory.addToEntry(
VerificationClassification.WARNING,
templateDecision(inputColumn.getVDmnDecision())
+ "Input column \"%s\" has no input data node.",
getColumnStringName(inputColumn));
}
private void searchPossibleInputData(VDmnInputColumn inputColumn) {
for (VDmnInputData inputData : dmnObjectContainer.getVDmnDefinition().getVDmnInputData()) {
if (inputData.getName().isPresent()
&& inputData.getName().get().equals(inputColumn.getName().get())) {
vreFactory.addVerificationFix(
VerificationFix.getBuilder()
.withFixName(new Name("Connect to Input Data " + inputData.getName().get()))
.addAction(
Action.getBuilder()
.withActionType(ActionType.UPDATE)
.withActionScope(ActionScope.INPUT_DATA)
.addIdValue(inputColumn.getVDmnDecision().getDecisionId())
.addIdValue(inputData.getInputDataId())
.build())
.build());
}
}
}
private void searchPossibleInputDecissions(VDmnInputColumn inputColumn) {
for (VDmnDecision decision : dmnObjectContainer.getVDmnDefinition().getVDmnDecisions()) {
for (VDmnOutputColumn outputColumn :
decision.getVDmnDecisionTable().getVDmnOutputColumns().stream()
.filter(
c ->
!c.getVDmnDecision()
.getDecisionId()
.equals(inputColumn.getVDmnDecision().getDecisionId())
&& c.getName().isPresent()
&& c.getName().get().equals(inputColumn.getName().get())
&& inputColumn.getTypeRef() == c.getTypeRef())
.collect(Collectors.toList())) {
vreFactory.addVerificationFix(
VerificationFix.getBuilder()
.withFixName(
new Name(
"Connect to Decision "
+ getDecisionStringName(outputColumn.getVDmnDecision())))
.addAction(
Action.getBuilder()
.withActionType(ActionType.UPDATE)
.withActionScope(ActionScope.DECISION)
.addIdValue(outputColumn.getVDmnDecision().getDecisionId())
.addValue(
"targetDecisionId",
inputColumn.getVDmnDecision().getDecisionId().getValue())
.build())
.build());
}
}
}
}
......@@ -26,6 +26,12 @@ p {
margin: 5px;
}
input[type=checkbox] {
transform: scale(1.5);
background-color: white;
margin: 10px;
}
.status-dot {
height: 15px;
width: 15px;
......@@ -201,7 +207,7 @@ span.select2 {
padding: 3px;
display: flex;
justify-content: flex-start;
align-items: flex-start;
align-items: center;
flex-wrap: wrap;
}
......
......@@ -20,12 +20,31 @@ let types = [];
let $verifierTypes;
let lastTypeSelect = undefined;
let $select = $(`
const $buttonReconnect = $(`
<button class="clickable dmn-verifier-header-item" id="dmn-button-reload-verifier"
title="Reconnect backend" onClick="loadAvailableTypes()">
Reconnect verification backend</button>
`);
const $buttonClean = $(`
<button class="clickable dmn-verifier-header-item" id="dmn-button-clean"
title="Request verifications" onClick="cleanHighlightFunction()">
Clean</button>
`);
const $select = $(`
<select name="verifier" id="verifier"
class="dmn-verifier-header-item dmn-verifier-select clickable"
title="Select calculated verification"
onchange="renderVerifierResult(this)">
`);
const $buttonVerify = $(`
<button class="clickable dmn-verifier-header-item" id="dmn-button-verify"
title="Request verifications" onClick="checkVerifications()">
Verify</button>
`);
const $checkBoxReverify = $(`
<input type="checkbox" name="reverify" checked>
`);
let $reverifyElement = $(`<label>Reverify</label>`).append($checkBoxReverify);
function handleStatus() {
let $dot = $('#backend-status');
......@@ -84,17 +103,9 @@ function cleanDmnVerifierRoot() {
`);
// check, if backend is available
if ($verifierTypes === undefined) {
$header.append($(`
<button class="clickable dmn-verifier-header-item" id="dmn-button-reload-verifier"
title="Reconnect backend" onClick="loadAvailableTypes()">
Reconnect verification backend</button>
`));
$header.append($buttonReconnect);
} else {
$header.append($(`
<button class="clickable dmn-verifier-header-item" id="dmn-button-clean"
title="Request verifications" onClick="cleanHighlightFunction()">
Clean</button>
`));
$header.append($buttonClean);
$header.append($verifierTypes);
$verifierTypes.select2({
containerCssClass: "dmn-verifier-header-item dmn-verifier-select clickable",
......@@ -106,11 +117,8 @@ function cleanDmnVerifierRoot() {
allowClear: true,
});
// Add Button Verifications
$header.append($(`
<button class="clickable dmn-verifier-header-item" id="dmn-button-verify"
title="Request verifications" onClick="checkVerifications()">
Verify</button>
`));
$header.append($buttonVerify);
$header.append($reverifyElement);
$select.empty();
$header.append($select);
}
......
......@@ -25,15 +25,15 @@ function getSheetElementWithId(id) {
function getRandomNewPointInCycle(sourceX, sourceY, w, offsetX, offsetY) {
let dx = Math.floor(Math.random() * Math.floor(2 * w)) - w;
let dy = Math.sqrt(w * w - dx * dx);
if (Math.random() > 0.5) {
dy *= -1;
}
if (offsetX) {
dx += offsetX;
}
if (offsetY) {
dy += offsetY;
}
if (Math.random() > 0.5) {
dy *= -1;
}
return {x: sourceX + dx, y: sourceY + dy};
}
......@@ -41,6 +41,8 @@ function getRandomNewPointInCycle(sourceX, sourceY, w, offsetX, offsetY) {
*
* @param {VerificationEntry} verificationEntry
* @param {VerificationFix} fix
* @param id fix row id
* @param $callerButton
*/
function performVerificationFix(verificationEntry, fix, id, $callerButton) {
console.log("FIX", fix);
......@@ -48,26 +50,33 @@ function performVerificationFix(verificationEntry, fix, id, $callerButton) {
cleanHighlightFunction();
$('.verification-container').removeClass('highlight-soft');
$('#ver-entry-' + id).addClass('highlight-soft');
let cud = false;
for (let i = 0; i < fix.actions.length; i++) {
switch (fix.actions[i].actionType) {
case 'SHOW':
performVerificationFixSHOW(verificationEntry, fix.actions[i]);
break;
case 'UPDATE':
alert("Action not defined: " + fix);
$callerButton.css('display', 'none');
break;
case 'CREATE':
performVerificationFixCREATE(verificationEntry, fix.actions[i]);
//$callerButton.css('display', 'none');
cud = true;
break;
case 'DELETE':
performVerificationFixDELETE(verificationEntry, fix.actions[i]);
$callerButton.css('display', 'none');
cud = true;
break;
case 'UPDATE':
performVerificationFixUPDATE(verificationEntry, fix.actions[i]);
cud = true;
break;
default:
alert("Action not defined: " + fix);
}
if (cud) {
$callerButton.css('display', 'none');
if ($checkBoxReverify.is(":checked")) {
checkVerifications();
}
}
performHighlightFunction();
}
}
......@@ -334,9 +343,80 @@ function deleteInputData(verificationEntry, fixAction) {
}
}
// get a rule:
// dmnModeler.getActiveViewer().get('modeling')._sheet._elementRegistry.get('DecisionRule_049xiav')
/**
*
* @param {VerificationEntry} verificationEntry
* @param {Action} fixAction
*/
function performVerificationFixUPDATE(verificationEntry, fixAction) {
switch (fixAction.actionScope) {
case 'INPUT_DATA':
updateInputData(verificationEntry, fixAction);
break;
case 'DECISION':
updateDecision(verificationEntry, fixAction);
break;
case 'RULE':
case 'INPUT_COLUMN':
case 'OUTPUT_COLUMN':
case 'INPUT_ENTRY':
case 'OUTPUT_ENTRY':
case 'DECISION_TABLE':
default:
alert("ACTION undefined: " + fixAction.actionType + ' -> '
+ fixAction.actionScope);
}
}
/**
*
* @param {VerificationEntry} verificationEntry
* @param {Action} fixAction
*/
function updateInputData(verificationEntry, fixAction) {
openViewWithId(verificationEntry.elements[0].identifier['definitionId']);
let inputData = getShapeWithId(fixAction.actionValues['inputDataId']);
if (fixAction.actionValues['decisionId']) {
const modeler = getCurrentModeler();
let decision = getShapeWithId(fixAction.actionValues['decisionId']);
getCurrentModeler().connect(inputData, decision,
{type: "dmn:InformationRequirement"})
}
if (fixAction.actionValues['name']) {
inputData.businessObject.name = fixAction.actionValues['name'];
}
openViewWithId(verificationEntry.elements[0].identifier['definitionId']);
}
/**
*
* @param {VerificationEntry} verificationEntry
* @param {Action} fixAction
*/
function updateDecision(verificationEntry, fixAction) {
openViewWithId(verificationEntry.elements[0].identifier['definitionId']);
let decision = getShapeWithId(
fixAction.actionValues['decisionId']);
if (fixAction.actionValues['targetDecisionId']) {
let targetDecision = getShapeWithId(
fixAction.actionValues['targetDecisionId']);
const modeler = getCurrentModeler();
getCurrentModeler().connect(decision, targetDecision,
{type: "dmn:InformationRequirement"})
}
if (fixAction.actionValues['name']) {
decision.businessObject.name = fixAction.actionValues['name'];
}
openViewWithId(verificationEntry.elements[0].identifier['definitionId']);
}
// get a rule:
/**
* Get a col by ID
* @param colId
* @param cells
* @returns {undefined|*}
*/
function getCellByColId(colId, cells) {
for (let i = 0; i < cells.length; i++) {
if (cells[i].col.id === colId) {
......
......@@ -49,6 +49,7 @@
<div id="metricContainer">
<h1>Metrics - Verification for Decision Model and Notation</h1>
<p>Token: <span id="token"></span></p>
<p><a href="metricsBi.html">Power BI</a></p>
<button id="loadData" onclick="loadData()">Reload statistics</button>
<button id="genToken" onclick="genToken()">Generate new token</button>
<canvas id="myChart"></canvas>
......
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Metrics - Verification for Decision Model and Notation</title>
<style>
body {
font-family: 'Open Sans', sans-serif;
}
table {
width: 100%;
margin: 20px auto;
table-layout: auto;
}
th {
text-align: left;
}
td {
padding: 2px;
padding-right: 10px;
}
tr > th {
border-bottom: solid 2px #a7a7a7;
}
tr > td {
border-bottom: solid 1px #a7a7a7;
}
#metricContainer {
max-width: 1300px;
padding-left: 0;
padding-right: 0;
margin-left: auto;
margin-right: auto;
display: block;
}
canvas {
border: solid 2px #a7a7a7;
}
</style>
</head>
<body>
<div id="metricContainer">
<iframe
allowFullScreen="true"
frameborder="0"
src="https://app.powerbi.com/view?r=eyJrIjoiOTY1Y2NhZDktMDE1OC00ZDJjLWI2YmUtNmY3YjBjNzAxYzk4IiwidCI6IjgwN2RlZTg1LTM3NDQtNDdiMC04YWU1LTlhMWM0ZGEwZjZmNSIsImMiOjEwfQ%3D%3D"
style="position:fixed; top:0; left:0; bottom:0; right:0; width:100%; height:100%; border:none; margin:0; padding:0; overflow:hidden; z-index:999999;"></iframe>
</div>
</body>
</html>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment