Merge pull request #369 from sartography/dmn-from-spreadsheet-395
Dmn from spreadsheet #395
This commit is contained in:
commit
787614ee91
31
crc/api.yml
31
crc/api.yml
|
@ -1030,6 +1030,37 @@ paths:
|
||||||
type: string
|
type: string
|
||||||
format: binary
|
format: binary
|
||||||
example: '<?xml version="1.0" encoding="UTF-8"?><bpmn:definitions></bpmn:definitions>'
|
example: '<?xml version="1.0" encoding="UTF-8"?><bpmn:definitions></bpmn:definitions>'
|
||||||
|
/dmn_from_ss:
|
||||||
|
# parameters:
|
||||||
|
# - name: workflow_spec_id
|
||||||
|
# in: query
|
||||||
|
# required: true
|
||||||
|
# description: The unique id of a workflow specification
|
||||||
|
# schema:
|
||||||
|
# type: string
|
||||||
|
post:
|
||||||
|
operationId: crc.api.file.dmn_from_ss
|
||||||
|
summary: Create a DMN table from a spreadsheet
|
||||||
|
tags:
|
||||||
|
- Files
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
multipart/form-data:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
file:
|
||||||
|
type: string
|
||||||
|
format: binary
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: DMN file for workflow spec
|
||||||
|
content:
|
||||||
|
application/octet-stream:
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: binary
|
||||||
|
example: '<?xml version="1.0" encoding="UTF-8"?><bpmn:definitions></bpmn:definitions>'
|
||||||
/task_events:
|
/task_events:
|
||||||
parameters:
|
parameters:
|
||||||
- name: action
|
- name: action
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import io
|
import io
|
||||||
|
from datetime import datetime
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
import connexion
|
import connexion
|
||||||
|
@ -171,3 +172,15 @@ def update_file_info(file_id, body):
|
||||||
|
|
||||||
def delete_file(file_id):
|
def delete_file(file_id):
|
||||||
FileService.delete_file(file_id)
|
FileService.delete_file(file_id)
|
||||||
|
|
||||||
|
|
||||||
|
def dmn_from_ss():
|
||||||
|
file = connexion.request.files['file']
|
||||||
|
result = FileService.dmn_from_spreadsheet(file)
|
||||||
|
return send_file(
|
||||||
|
io.BytesIO(result),
|
||||||
|
attachment_filename='temp_dmn.dmn',
|
||||||
|
mimetype='text/xml',
|
||||||
|
cache_timeout=-1, # Don't cache these files on the browser.
|
||||||
|
last_modified=datetime.now()
|
||||||
|
)
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import io
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from github import Github, GithubObject, UnknownObjectException
|
from github import Github, GithubObject, UnknownObjectException
|
||||||
|
@ -436,3 +439,109 @@ class FileService(object):
|
||||||
branch=target_branch
|
branch=target_branch
|
||||||
)
|
)
|
||||||
return {'updated': True}
|
return {'updated': True}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def dmn_from_spreadsheet(ss_data):
|
||||||
|
|
||||||
|
def _get_random_string(length):
|
||||||
|
return ''.join(
|
||||||
|
[random.choice(string.ascii_letters + string.digits) for n in range(length)])
|
||||||
|
|
||||||
|
def _row_has_value(values):
|
||||||
|
for value_item in values:
|
||||||
|
if not pd.isnull(value_item):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
df = pd.read_excel(io.BytesIO(ss_data.read()), header=None)
|
||||||
|
|
||||||
|
xml_ns = "https://www.omg.org/spec/DMN/20191111/MODEL/"
|
||||||
|
dmndi_ns = "https://www.omg.org/spec/DMN/20191111/DMNDI/"
|
||||||
|
dc_ns = "http://www.omg.org/spec/DMN/20180521/DC/"
|
||||||
|
dmndi = "{%s}" % dmndi_ns
|
||||||
|
dc = "{%s}" % dc_ns
|
||||||
|
nsmap = {None: xml_ns, 'dmndi': dmndi_ns, 'dc': dc_ns}
|
||||||
|
|
||||||
|
root = etree.Element("definitions",
|
||||||
|
id="Definitions",
|
||||||
|
name="DRD",
|
||||||
|
namespace="http://camunda.org/schema/1.0/dmn",
|
||||||
|
nsmap=nsmap,
|
||||||
|
)
|
||||||
|
|
||||||
|
decision_name = df.iat[0, 1]
|
||||||
|
decision_id = df.iat[1, 1]
|
||||||
|
decision = etree.SubElement(root, "decision",
|
||||||
|
id=decision_id,
|
||||||
|
name=decision_name
|
||||||
|
)
|
||||||
|
decision_table = etree.SubElement(decision, 'decisionTable', id='decisionTable_1')
|
||||||
|
input_output = df.iloc[2][1:]
|
||||||
|
count = 1
|
||||||
|
input_count = 1
|
||||||
|
output_count = 1
|
||||||
|
for item in input_output:
|
||||||
|
if item == 'Input':
|
||||||
|
label = df.iloc[3, count]
|
||||||
|
input_ = etree.SubElement(decision_table, 'input', id=f'input_{input_count}', label=label)
|
||||||
|
type_ref = df.iloc[5, count]
|
||||||
|
input_expression = etree.SubElement(input_, 'inputExpression', id=f'inputExpression_{input_count}',
|
||||||
|
typeRef=type_ref)
|
||||||
|
expression = df.iloc[4, count]
|
||||||
|
expression_text = etree.SubElement(input_expression, 'text')
|
||||||
|
expression_text.text = expression
|
||||||
|
|
||||||
|
input_count += 1
|
||||||
|
elif item == 'Output':
|
||||||
|
label = df.iloc[3, count]
|
||||||
|
name = df.iloc[4, count]
|
||||||
|
type_ref = df.iloc[5, count]
|
||||||
|
decision_table.append(etree.Element('output', id=f'output_{output_count}',
|
||||||
|
label=label, name=name, typeRef=type_ref))
|
||||||
|
output_count += 1
|
||||||
|
elif item == 'Annotation':
|
||||||
|
break
|
||||||
|
count += 1
|
||||||
|
|
||||||
|
row = 6
|
||||||
|
column_count = count
|
||||||
|
while row < df.shape[0]:
|
||||||
|
column = 1
|
||||||
|
row_values = df.iloc[row].values[1:column_count]
|
||||||
|
if _row_has_value(row_values):
|
||||||
|
rando = _get_random_string(7).lower()
|
||||||
|
rule = etree.SubElement(decision_table, 'rule', id=f'DecisionRule_{rando}')
|
||||||
|
|
||||||
|
i = 1
|
||||||
|
while i < input_count:
|
||||||
|
input_entry = etree.SubElement(rule, 'inputEntry', id=f'UnaryTests_{_get_random_string(7)}')
|
||||||
|
text_element = etree.SubElement(input_entry, 'text')
|
||||||
|
text_element.text = str(df.iloc[row, column]) if not pd.isnull(df.iloc[row, column]) else ''
|
||||||
|
i += 1
|
||||||
|
column += 1
|
||||||
|
i = 1
|
||||||
|
while i < output_count:
|
||||||
|
output_entry = etree.SubElement(rule, 'outputEntry', id=f'LiteralExpression_{_get_random_string(7)}')
|
||||||
|
text_element = etree.SubElement(output_entry, 'text')
|
||||||
|
text_element.text = str(df.iloc[row, column]) if not pd.isnull(df.iloc[row, column]) else ''
|
||||||
|
i += 1
|
||||||
|
column += 1
|
||||||
|
|
||||||
|
description = etree.SubElement(rule, 'description')
|
||||||
|
text = df.iloc[row, column] if not pd.isnull(df.iloc[row, column]) else ''
|
||||||
|
description.text = text
|
||||||
|
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
dmndi_root = etree.SubElement(root, dmndi + "DMNDI")
|
||||||
|
dmndi_diagram = etree.SubElement(dmndi_root, dmndi + "DMNDiagram")
|
||||||
|
# rando = _get_random_string(7).lower()
|
||||||
|
dmndi_shape = etree.SubElement(dmndi_diagram, dmndi + "DMNShape",
|
||||||
|
dmnElementRef=decision_id)
|
||||||
|
bounds = etree.SubElement(dmndi_shape, dc + "Bounds",
|
||||||
|
height='80', width='180', x='100', y='100')
|
||||||
|
|
||||||
|
prefix = b'<?xml version="1.0" encoding="UTF-8"?>'
|
||||||
|
dmn_file = prefix + etree.tostring(root)
|
||||||
|
|
||||||
|
return dmn_file
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,27 @@
|
||||||
|
from tests.base_test import BaseTest
|
||||||
|
|
||||||
|
from crc import app
|
||||||
|
|
||||||
|
import io
|
||||||
|
from lxml import etree
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
class TestDMNFromSS(BaseTest):
|
||||||
|
|
||||||
|
def test_dmn_from_ss(self):
|
||||||
|
|
||||||
|
filepath = os.path.join(app.root_path, '..', 'tests', 'data',
|
||||||
|
'dmn_from_spreadsheet', 'large_test_spreadsheet.xlsx')
|
||||||
|
f_handle = open(filepath, 'br')
|
||||||
|
ss_data = f_handle.read()
|
||||||
|
|
||||||
|
data = {'file': (io.BytesIO(ss_data), 'test.xlsx')}
|
||||||
|
rv = self.app.post('/v1.0/dmn_from_ss', data=data, follow_redirects=True,
|
||||||
|
content_type='multipart/form-data', headers=self.logged_in_headers())
|
||||||
|
|
||||||
|
dmn_xml = rv.stream.response.data
|
||||||
|
root = etree.fromstring(dmn_xml)
|
||||||
|
children = root.getchildren()
|
||||||
|
self.assertEqual('{https://www.omg.org/spec/DMN/20191111/MODEL/}decision', children[0].tag)
|
||||||
|
self.assertEqual('{https://www.omg.org/spec/DMN/20191111/DMNDI/}DMNDI', children[1].tag)
|
Loading…
Reference in New Issue