draw io family tree template

I couple of years ago I wrote an article about Business Rule Engines and their importance. I will not repeat myself here why the Business Rules concept is that important. Today's dynamic nature of the business and the complex interactions involved among the businesses and their customers require clear and declarative way to define rules for customer classification, product classification, user behavior classification, outlier and anomaly detection etc.

Decision trees are very powerful business rules representation. Users, systems and applications use decision trees for entity (product, customer etc.) classification based on the entity's attributes. They are easy to follow and they are self-explanatory. Non-technical and business users can easily understand or even create these rules in some graphical UI workbench.

Off course, there are commercial and expensive Business Rules products that cost a lot of money. Let us focus on the open source tools and libraries. When we talk about Business Rules, it is very important if they have been implemented using the Rete algorithm. Some of these open source libraries are Drools and Nools. We will focus on Nools in this article. It is an open source rule engine written in JavaScript. This engine is very similar with Drools; however, UI workbench does not exist.

What do we have at our disposal for diagrams creation? I have "met" Draw.io a couple of years ago while I was listening one of the Ryan Kroonenburg's AWS courses. I love this tool. It has wide variety of features and possibility to create plethora of diagrams. Since recently, Draw.io offers Desktop application as well.  In addition, Draw.io allows us to easily create and install plugins written in JavaScript. Well, let us use that feature and create Nools Decision Tree Generator plugin!

How Nools rules look like?

define Product {  F1: 0,  F2: 0,  F3: 0,  result:'',  constructor: function(F1,F2,F3) {   this.F1=F1;   this.F2=F2;   this.F3=F3;  } }  rule 'Rule 0' {     when {         p : Product (p.F1<50 AND p.F2<40 AND p.F1<20);     }     then {         p.result='A';     } }  rule 'Rule 1' {     when {         p : Product (p.F1<50 AND p.F2<40 AND p.F1>=20);     }     then {         p.result='B';     }        
}        

Our goal in this article is to create a plugin that will allow us to draw Decision Trees in Draw.io (Desktop) and export (save) the generated Nools rules.

No alt text provided for this image

The representation is very simple: we specify conditions as text on the edges ( for example p.F1 < 50); the vertices explain what attribute is checked at particular level. The leaf vertices represent the result (classification category for example).

In order to create rules we need a small piece of additional metadata. That metadata can be entered as metadata properties at diagram level. For our purpose we need:

  • Definitions: Fact's structure and its attributes
  • Fact type: Used when creating the rules
  • Fact name: Used when creating the rules

No alt text provided for this image

We have configured everything required for rules generation. Let us create the plugin.

Every Draw.io plugin has the following basic structure:

Draw.loadPlugin(function(uiEditor) {        
});        

First, within the body of the plugin, we will define some utility functions required further in the decision tree generation logic: de-entitizing HTML text and value extraction from JavaScript object.

function deentitize(s) {     var ret = s.replace(/&gt;/g, '>');     ret = ret.replace(/&lt;/g, '<');     ret = ret.replace(/&quot;/g, '"');     ret = ret.replace(/&apos;/g, "'");     ret = ret.replace(/&amp;/g, '&');     return ret; };  function getValue(val) {     if(typeof(val)=='object')         return val.attributes.getNamedItem('label').value;     else         return val;        
}        

Next, we will build the Tree from the Draw.io model provided (uiEditor.editor.graph.model). In essence, the model contains cells that are annotated with several attributes that explain if the cell is vertex or edge, the label of the cell, source/target when dealing with edges etc.

In our particular case, we shall find, define and separate the vertices, edges as well as the root of the tree:

function getDecisionTree(model) {              const vertices=[];     const edges=[];          const metadata={         factName: '',         factType: '',         definitions: ''     };          Object.keys(model.cells).forEach((x)=> {         const cell=model.cells[x];         if(cell.vertex)             vertices.push(cell);         if(cell.edge)             edges.push(cell);         if(cell.id==0){             const attributes=cell.value.attributes;             if(attributes!=null){                 metadata.factName=attributes.getNamedItem('factName').value;                 metadata.factType=attributes.getNamedItem('factType').value;                 metadata.definitions=attributes.getNamedItem('definitions').value;             }         }     });                       const vertexMap={};          vertices.forEach((vertex)=> {         const id=vertex.id;         const value=getValue(vertex.value);         vertexMap[id]={id:id, value:value, nodeParent:null};     });          edges.forEach((edge) => {         const source=edge.source.id         const target=edge.target.id;         const value=getValue(edge.value);         vertexMap[target].nodeParent=vertexMap[source].id;         vertexMap[target].sourceExpression=deentitize(value);         if(vertexMap[source].children==null)             vertexMap[source].children=[vertexMap[target]];         else             vertexMap[source].children.push(vertexMap[target]);     });          //now find the root of the decision tree     let root=null;     Object.values(vertexMap).forEach((vertex)=> {         if(vertex.nodeParent==null)             root=vertex;     });          return {root,metadata};        
}        

At this stage we have the Tree representation. Let us build the rules from that Tree representation:

function buildRules(decisionTree) {     const stack=[];     const rules=[];     const root=decisionTree.root;     const metadata=decisionTree.metadata;     traverseNode(root,stack,rules,metadata);     let rulesDef=rules.join('\n\n');     rulesDef=metadata.definitions+'\n\n'+rulesDef;     return rulesDef; }        

The "heart" of the generation logic is the recursive function traverseNode:

function traverseNode(node,stack,rules,metadata) {     let expressionPushed=false;     if(node.children!=null && node.children.length>0){         if(node.sourceExpression!=null){             stack.push(node.sourceExpression);             expressionPushed=true;         }         node.children.forEach((n)=> {             traverseNode(n,stack,rules,metadata);         });     }     else {         if(node.sourceExpression!=null){             stack.push(node.sourceExpression);             expressionPushed=true;         }         const statement=stack.join(' AND ');         let rule='rule \'Rule '+rules.length+'\' {\n';         rule+='\twhen {\n';         rule+='\t\t'+metadata.factName+' : '+metadata.factType+' ('+statement+');\n';         rule+='\t}\n';         rule+='\tthen {\n';         rule+='\t\t'+metadata.factName+'.result=\''+node.value+'\';\n';         rule+='\t}\n';         rule+='}';         rules.push(rule);     }     if(expressionPushed)         stack.pop();        
}        

Finally, we shall wire our Nools rule generation logic with the UI. We want to create our own menu before the last one ('Help') and one action that will trigger our save/export functionality.

//Add action mxResources.parse('generateNoolsRules=Generate Nools Rules...');      uiEditor.actions.addAction('generateNoolsRules', function() {      const decisionTree=getDecisionTree(uiEditor.editor.graph.model);              const rules=buildRules(decisionTree);          uiEditor.saveData('decision-tree.nools', 'nools', rules, 'text/plain');      });   //Add IW Menu //Find the last non-empty div in the menu  let insertBeforeDiv=null; for(let i=uiEditor.menubar.container.childNodes.length-1;i>=0;i--){     const div=uiEditor.menubar.container.childNodes[i];     if(div.text!=null && div.text!=''){         insertBeforeDiv=div;         break;     } }  uiEditor.menubar.addMenu('\u22eeIW', function(menu, parent) {         uiEditor.menus.addMenuItems(menu, ['generateNoolsRules'], parent);     },insertBeforeDiv);        

The plugin is ready to be installed. Start Draw.io Desktop, navigate to Extras/Plugins and install the JavaScript plugin code we have created; restart the application after that.

No alt text provided for this image

The same principles can be applied for various modeling scenarios. Knowing the Draw.io graph model will help you implement arbitrary modeling techniques with export functionality not covered elsewhere. InterWorks can help you brainstorm, design and implement similar convenient solutions. We have done that for the others. We will do it for you as well!

Till the next time!

Marjan

gulloverl1958.blogspot.com

Source: https://www.linkedin.com/pulse/drawio-decision-tree-generator-marjan-sterjev

0 Response to "draw io family tree template"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel