Merge pull request #369 from sartography/dmn-from-spreadsheet-395

Dmn from spreadsheet #395
This commit is contained in:
Mike Cullerton 2021-09-14 17:41:01 -04:00 committed by GitHub
commit 787614ee91
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 180 additions and 0 deletions

View File

@ -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

View File

@ -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()
)

View File

@ -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

View File

@ -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)