Skip to content

An Azure implementation of DecisionCentral - a central repository for DMN decision services

License

Notifications You must be signed in to change notification settings

russellmcdonell/DecisionCentralAzure

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

DecisionCentralAzure

An Azure compatible, flask implementation of DecisionCentral

DecisionCentralAzure is a central repository for all DMN based decision services.

DecisionCentralAzure

  • Creates a web site that lets you upload DMN compliant Excel workbooks or DMN conformant XML files
  • For each uploaded DMN compliant Excel workbook or DMN compliant XML file, DecisionCentral will
    • create a decision service from that DMN compliant Excel workbook or DMN conformant XML file
    • create a user interface where users can enter values and check specific use cases for this decision service
    • creates an API for this decision service which accepts JSON data (the input values) and returns a JSON structure (representing the decision)
  • For each decision service, DecisionCentral will create web pages detailing all the parts of your decision service
    • The glossary of data items, both inputs and outputs, associated with this decision service
    • The decision sequence (the sequence in which you decision tables will be run)
    • The decision tables that form this decision service
    • An OpenAPI specification for the the API associated with this decision service which will be displayed as a web page, but it can also be downloaded and imported to Postman/Swagger etc.
  • For each decision table, within each decision service, DecisionCentral will
    • create a DMN compliant representation of the rules built when the decision service was created
    • create a user interface where users can enter values and check specific use cases for this decision table within this decision service
    • create an API for this decision table within this decision service which accepts JSON data (the input values) and returns a JSON structure (representing the decision)
    • createe an OpenAPI specification for the the API associated with this decision table with this decision service which will be displayed as a web page, but it can also be downloaded and imported to Postman/Swagger etc.

DecisionCentralAzure also has an API for uploading a DMN compliant Excel workbook or DMN conformant XML file, plus and API for deleting a decision service.

This version allows you to stand up a version of [DecisionCentral (https://github.com/russellmcdonell/DecisionCentral) in Azure. Being the flask version, it will create a web site and API services on port 5000.

The process should be

  • Clone this repository
  • Deploy to your Azure App Service ( use Azure add-ins for Visual Studio Code )

If that process doesn't work for you, but you find another one that does, then please let me know ( russell.mcdonell@c-cost.com )

JSON and DMN data types JSON doesn't support all the data types that are supported by DMN (and the FEEL expression language). Decision Central borrows a solution suggested by FEEL - @strings. If a string starts with the two characters @" and ends with the character " then what is in between has to be de-serialized by some other non-JSON interpreter. In this case, the FEEL interpreter (pySFeel). The following code can be used to serialize and de-serialize @strings.

import datetime
import pySFeel

parser = pySFeel.SFeelParser()


def convertAtString(thisString):
    # Convert an @string
    (status, newValue) = parser.sFeelParse(thisString[2:-1])
    if 'errors' in status:
        return thisString
    else:
        return newValue


def convertIn(newValue):
    if isinstance(newValue, dict):
        for key in newValue:
            if isinstance(newValue[key], int):
                newValue[key] = float(newValue[key])
            elif isinstance(newValue[key], str) and (newValue[key][0:2] == '@"') and (newValue[key][-1] == '"'):
                newValue[key] = convertAtString(newValue[key])
            elif isinstance(newValue[key], dict) or isinstance(newValue[key], list):
                newValue[key] = convertIn(newValue[key])
    elif isinstance(newValue, list):
        for i in range(len(newValue)):
            if isinstance(newValue[i], int):
                newValue[i] = float(newValue[i])
            elif isinstance(newValue[i], str) and (newValue[i][0:2] == '@"') and (newValue[i][-1] == '"'):
                newValue[i] = convertAtString(newValue[i])
            elif isinstance(newValue[i], dict) or isinstance(newValue[i], list):
                newValue[i] = convertIn(newValue[i])
    elif isinstance(newValue, str) and (newValue[0:2] == '@"') and (newValue[-1] == '"'):
        newValue = convertAtString(newValue)
    return newValue


  def convertOut(thisValue):
      if isinstance(thisValue, datetime.date):
          return '@"' + thisValue.isoformat() + '"'
      elif isinstance(thisValue, datetime.datetime):
          return '@"' + thisValue.isoformat(sep='T') + '"'
      elif isinstance(thisValue, datetime.time):
          return '@"' + thisValue.isoformat() + '"'
      elif isinstance(thisValue, datetime.timedelta):
          sign = ''
          duration = thisValue.total_seconds()
          if duration < 0:
              duration = -duration
              sign = '-'
          secs = duration % 60
          duration = int(duration / 60)
          mins = duration % 60
          duration = int(duration / 60)
          hours = duration % 24
          days = int(duration / 24)
          return '@"%sP%dDT%dH%dM%fS"' % (sign, days, hours, mins, secs)
      elif isinstance(thisValue, bool):
          return thisValue:
      elif thisValue is None:
          return thisValue:
      elif isinstance(thisValue, int):
          sign = ''
          if thisValue < 0:
              thisValue = -thisValue
              sign = '-'
          years = int(thisValue / 12)
          months = (thisValue % 12)
          return '@"%sP%dY%dM"' % (sign, years, months)
      elif isinstance(thisValue, tuple) and (len(thisValue) == 4):
          (lowEnd, lowVal, highVal, highEnd) = thisValue
          return '@"' + lowEnd + str(lowVal) + ' .. ' + str(highVal) + highEnd
      elif thisValue is None:
          return 'null'
      elif isinstance(thisValue, dict):
          for item in thisValue:
              thisValue[item] = convertOut(thisValue[item])
          return thisValue
      elif isinstance(thisValue, list):
          for i in range(len(thisValue)):
              thisValue[i] = convertOut(thisValue[i])
          return thisValue
      else:
          return thisValue

DecisionCentralAzure is not, of itself, a production product. You use pyDMNrules to build those.
It is intended for use at Hackathons and Connectathons; anywhere you need a complex decision service created quickly and easily.

Releases

No releases published

Packages

No packages published

Languages