├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── MANIFEST.in
├── Pipfile
├── README.md
├── README.rst
├── __init__.py
├── bpmn_python
├── __init__.py
├── bpmn_diagram_exception.py
├── bpmn_diagram_export.py
├── bpmn_diagram_import.py
├── bpmn_diagram_layouter.py
├── bpmn_diagram_metrics.py
├── bpmn_diagram_rep.py
├── bpmn_diagram_visualizer.py
├── bpmn_import_utils.py
├── bpmn_process_csv_export.py
├── bpmn_process_csv_import.py
├── bpmn_python_consts.py
├── diagram_layout_metrics.py
├── graph
│ ├── __init__.py
│ └── classes
│ │ ├── __init__.py
│ │ ├── activities
│ │ ├── __init__.py
│ │ ├── activity_type.py
│ │ ├── subprocess_type.py
│ │ └── task_type.py
│ │ ├── base_element_type.py
│ │ ├── condition_expression_type.py
│ │ ├── events
│ │ ├── __init__.py
│ │ ├── catch_event_type.py
│ │ ├── end_event_type.py
│ │ ├── event_type.py
│ │ ├── intermediate_catch_event_type.py
│ │ ├── intermediate_throw_event_type.py
│ │ ├── start_event_type.py
│ │ └── throw_event_type.py
│ │ ├── flow_element_type.py
│ │ ├── flow_node_type.py
│ │ ├── gateways
│ │ ├── __init__.py
│ │ ├── exclusive_gateway_type.py
│ │ ├── gateway_type.py
│ │ ├── inclusive_gateway_type.py
│ │ └── parallel_gateway_type.py
│ │ ├── lane_set_type.py
│ │ ├── lane_type.py
│ │ ├── message_flow_type.py
│ │ ├── participant_type.py
│ │ ├── root_element
│ │ ├── __init__.py
│ │ ├── callable_element_type.py
│ │ ├── event_definition_type.py
│ │ ├── process_type.py
│ │ └── root_element_type.py
│ │ └── sequence_flow_type.py
└── grid_cell_class.py
├── docs
├── Makefile
├── image
│ ├── camunda-complex-example.png
│ ├── camunda-simple-example.png
│ ├── manual-complex.png
│ ├── manual-simple.png
│ ├── signavio-complex-example.png
│ └── signavio-simple-example.png
├── requirements-docs.txt
└── source
│ ├── api
│ ├── bpmn_diagram_exception.rst
│ ├── bpmn_diagram_export.rst
│ ├── bpmn_diagram_import.rst
│ ├── bpmn_diagram_layouter.rst
│ ├── bpmn_diagram_metrics.rst
│ ├── bpmn_diagram_rep.rst
│ ├── bpmn_diagram_visualizer.rst
│ ├── bpmn_process_csv_export.rst
│ ├── bpmn_process_csv_import.rst
│ └── grid_cell_class.rst
│ ├── api_reference.rst
│ ├── conf.py
│ └── index.rst
├── requirements.txt
├── setup.cfg
├── setup.py
├── tests
├── conftest.py
├── csv_export
│ └── csv_export_test.py
├── csv_import
│ ├── csv_import_test.py
│ └── input
│ │ ├── airline-checkin.csv
│ │ ├── order-processing.csv
│ │ └── pizza-order.csv
├── examples
│ ├── csv_export
│ │ ├── bank-account-process.bpmn
│ │ ├── checkin-process.bpmn
│ │ ├── credit-process.bpmn
│ │ ├── order-processing.bpmn
│ │ ├── pizza-order.bpmn
│ │ └── tram-process.bpmn
│ ├── metrics
│ │ ├── crossing_point_test.bpmn
│ │ └── cycles_test.bpmn
│ └── xml_import_export
│ │ ├── bpmn_editor_simple_example.xml
│ │ ├── camunda_complex_example.bpmn
│ │ ├── camunda_simple_example.bpmn
│ │ ├── default-conditional-flow-example.bpmn
│ │ ├── lanes.bpmn
│ │ ├── signavio_complex_example.bpmn
│ │ └── signavio_simple_example.bpmn
├── layouter
│ └── layouter_test.py
├── metrics
│ ├── layout_metrics_test.py
│ └── test_bpmn_diagram_metrics.py
└── xml_import_export
│ ├── bpmn_editor_import_test.py
│ ├── camunda_complex_test.py
│ ├── camunda_simple_test.py
│ ├── default_conditional_flow_test.py
│ ├── lanes_test.py
│ ├── manually_generated_complex_diagram.py
│ ├── manually_generated_simple_diagram.py
│ ├── signavio_complex_test.py
│ └── signavio_simple_test.py
└── tox.ini
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | env/
12 | ENV/
13 | build/
14 | develop-eggs/
15 | dist/
16 | downloads/
17 | eggs/
18 | .eggs/
19 | lib/
20 | lib64/
21 | parts/
22 | sdist/
23 | var/
24 | local/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .coverage
43 | .coverage.*
44 | .cache
45 | nosetests.xml
46 | coverage.xml
47 | *,cover
48 | .hypothesis/
49 |
50 | # Translations
51 | *.mo
52 | *.pot
53 |
54 | # Django stuff:
55 | *.log
56 |
57 | # Sphinx documentation
58 | docs/_build/
59 |
60 | # PyBuilder
61 | target/
62 |
63 | #Ipython Notebook
64 | .ipynb_checkpoints
65 |
66 | #Other
67 | tests/xml_import_export/output/**
68 | .idea
69 | tests/output/
70 | tests/layouter/output/
71 | tests/csv_export/output/
72 | tests/xml_import_export/output/
73 | tests/csv_import/output/
74 | **/*.sw[po]
75 | Pipfile.lock
76 | _trial_temp
77 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
3 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
4 |
5 | ## [0.0.19] - 2019-01-05
6 | ### Added
7 | - basic changelog with release dates
8 | ### Changed
9 | - setup.py entries to make description in pypi more readable
10 | - setup.py install requires now reads needed modules from requirements.txt
11 |
12 | ## [0.0.18] - 2017-07-14
13 | ## [0.0.17] - 2017-07-14
14 | ## [0.0.16] - 2017-07-13
15 | ## [0.0.15] - 2017-07-07
16 | ## [0.0.14] - 2017-06-28
17 | ## [0.0.12] - 2017-06-23
18 | ## [0.0.11] - 2017-03-28
19 | ## [0.0.10] - 2017-03-28
20 | ## [0.0.9] - 2017-03-28
21 | ## [0.0.8] - 2017-03-28
22 | ## [0.0.7] - 2017-03-28
23 | ## [0.0.6] - 2017-03-28
24 | ## [0.0.5] - 2017-03-28
25 | ## [0.0.4] - 2017-03-28
26 | ## [0.0.3] - 2017-02-20
27 | ## [0.0.2] - 2016-09-18
28 | ## [0.0.1] - 2016-09-08
29 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include README.md
2 | include README.rst
3 | include LICENSE
4 | include CHANGELOG.md
5 | include requirements.txt
6 |
--------------------------------------------------------------------------------
/Pipfile:
--------------------------------------------------------------------------------
1 | [[source]]
2 | name = "pypi"
3 | url = "https://pypi.org/simple"
4 | verify_ssl = true
5 |
6 | [dev-packages]
7 | pytest = "*"
8 | pytest-cov = "*"
9 | tox = "*"
10 |
11 | [packages]
12 | bpmn-python = {editable = true,path = "."}
13 |
14 | [requires]
15 | python_version = "3.7"
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # bpmn-python
2 | Project for creating a Python library that allows to import/export BPMN diagram (as an XML file) and provides a simple visualization capabilities
3 |
4 | Project structure
5 | * bpmn_python - main module of project, includes all source code
6 | * tests - unit tests for package
7 | * examples - examples of XML files used in tests
8 | * docs - documentation for package
9 |
10 |
11 | ## Development
12 |
13 | Requirements: [pipenv](https://pipenv.readthedocs.io/en/latest/), Python 3.7. If you do not have Python 3.7 installed, consider using [pyenv](https://github.com/pyenv/pyenv). After setting up, it integrates with pipenv allowing latter to automatically pull correct Python version for use in virtual environment.
14 |
15 | To set up local development environment, clone the repository, enter it and execute:
16 | ```bash
17 | pipenv install --dev -e .
18 | pipenv shell
19 | ```
20 |
21 | Run tests with HTML coverage report:
22 | ```bash
23 | pytest --cov-report html --cov=bpmn_python
24 | ```
25 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | bpmn-python
2 | ===========
3 |
4 | Project for creating a Python library that allows to import/export BPMN
5 | diagram (as an XML file) and provides a simple visualization
6 | capabilities
7 |
8 | Project structure \* bpmn\_python - main module of project, includes all
9 | source code \* tests - unit tests for package \* examples - examples of
10 | XML files used in tests \* docs - documentation for package
11 |
--------------------------------------------------------------------------------
/__init__.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Package init file
4 | """
5 | import bpmn_python
6 |
--------------------------------------------------------------------------------
/bpmn_python/__init__.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Package init file
4 | """
5 | __all__ = ["bpmn_diagram_export", "bpmn_diagram_import", "bpmn_diagram_layouter",
6 | "bpmn_diagram_exception", "bpmn_diagram_metrics", "bpmn_diagram_visualizer", "bpmn_import_utils",
7 | "bpmn_process_csv_export", "diagram_layout_metrics", "grid_cell_class", "bpmn_diagram_rep"]
8 |
--------------------------------------------------------------------------------
/bpmn_python/bpmn_diagram_exception.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Custom exception class for bpmn_python
4 | """
5 |
6 |
7 | class BpmnPythonError(Exception):
8 | """
9 | Custom BpmnPythonError exception
10 | """
11 | def __init__(self, value):
12 | self.value = value
13 |
14 | def __str__(self):
15 | return repr(self.value)
16 |
--------------------------------------------------------------------------------
/bpmn_python/bpmn_diagram_metrics.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Package provides set of functions for calculating complexity metrics
4 | of BPMN 2.0 XML models represented by objects of BpmnDiagramGraph class.
5 | """
6 |
7 | from collections import Counter
8 |
9 | from math import sqrt
10 |
11 | GATEWAY_TYPES = ['inclusiveGateway', 'exclusiveGateway', 'parallelGateway', 'eventBasedGateway', 'complexGateway']
12 | EVENT_TYPES = ['startEvent', 'endEvent', 'intermediateCatchEvent', 'intermediateThrowEvent']
13 |
14 |
15 | def get_nodes_count(bpmn_graph, node_type=None):
16 | """
17 | Gets the count of nodes of the requested type.
18 | If no type is provided,
19 | the count of all nodes in BPMN diagram graph is returned.
20 |
21 | :param bpmn_graph: an instance of BpmnDiagramGraph representing BPMN model.
22 | :param node_type: string with valid BPMN XML tag name
23 | (e.g. 'task', 'sequenceFlow').
24 | """
25 |
26 | return len(bpmn_graph.get_nodes(node_type=node_type))
27 |
28 |
29 | def get_all_gateways(bpmn_graph):
30 | """
31 | Returns a list with all gateways in diagram
32 |
33 | :param bpmn_graph: an instance of BpmnDiagramGraph representing BPMN model.
34 | :return: a list with all gateways in diagram
35 | """
36 | gateways = filter(lambda node: node[1]['type'] in GATEWAY_TYPES, bpmn_graph.get_nodes())
37 |
38 | return gateways
39 |
40 |
41 | def get_gateway_counts(bpmn_graph):
42 | """
43 | Returns the count of the different types of gateways
44 | in the BPMNDiagramGraph instance.
45 |
46 | :param bpmn_graph: an instance of BpmnDiagramGraph representing BPMN model.
47 | :return: count of the different types of gateways in the BPMNDiagramGraph instance
48 | """
49 |
50 | return {gateway_type: get_nodes_count(bpmn_graph, node_type=gateway_type)
51 | for gateway_type in GATEWAY_TYPES}
52 |
53 |
54 | def get_events_counts(bpmn_graph):
55 | """
56 | Returns the count of the different types of event elements
57 | in the BPMNDiagramGraph instance.
58 |
59 | :param bpmn_graph: an instance of BpmnDiagramGraph representing BPMN model.
60 | :return: count of the different types of event elements in the BPMNDiagramGraph instance
61 | """
62 |
63 | return {event_type: get_nodes_count(bpmn_graph, node_type=event_type)
64 | for event_type in EVENT_TYPES}
65 |
66 |
67 | def get_activities_counts(bpmn_graph):
68 | """
69 | Returns the count of the different types of activities
70 | in the BPMNDiagramGraph instance.
71 |
72 | :param bpmn_graph: an instance of BpmnDiagramGraph representing BPMN model.
73 | :return: count of the different types of activities in the BPMNDiagramGraph instance
74 | """
75 |
76 | return {
77 | "task": get_nodes_count(bpmn_graph,
78 | node_type="task"),
79 | "subProcess": get_nodes_count(bpmn_graph,
80 | node_type="subProcess"),
81 | }
82 |
83 |
84 | def all_activities_count(bpmn_graph):
85 | """
86 | Returns the total count of all activities in the BPMNDiagramGraph instance.
87 |
88 | :param bpmn_graph: an instance of BpmnDiagramGraph representing BPMN model.
89 | :return: total count of the activities in the BPMNDiagramGraph instance
90 | """
91 |
92 | return sum([
93 | count for name, count in get_activities_counts(bpmn_graph).items()
94 | ])
95 |
96 |
97 | def all_gateways_count(bpmn_graph):
98 | """
99 | Returns the total count of all gateway elements
100 | in the BPMNDiagramGraph instance.
101 |
102 | :param bpmn_graph: an instance of BpmnDiagramGraph representing BPMN model.
103 | :return: total count of the gateway elements in the BPMNDiagramGraph instance
104 | """
105 |
106 | return sum([
107 | count for name, count in get_gateway_counts(bpmn_graph).items()
108 | ])
109 |
110 |
111 | def all_control_flow_elements_count(bpmn_graph):
112 | """
113 | Returns the total count of all control flow elements
114 | in the BPMNDiagramGraph instance.
115 |
116 |
117 | :param bpmn_graph: an instance of BpmnDiagramGraph representing BPMN model.
118 | :return: total count of the control flow elements in the BPMNDiagramGraph instance
119 | """
120 |
121 | gateway_counts = get_gateway_counts(bpmn_graph)
122 | events_counts = get_events_counts(bpmn_graph)
123 | control_flow_elements_counts = gateway_counts.copy()
124 | control_flow_elements_counts.update(events_counts)
125 |
126 | return sum([
127 | count for name, count in control_flow_elements_counts.items()
128 | ])
129 |
130 |
131 | def all_events_count(bpmn_graph):
132 | """
133 | Returns the total count of all events elements
134 | in the BPMNDiagramGraph instance.
135 |
136 | :param bpmn_graph: an instance of BpmnDiagramGraph representing BPMN model.
137 | :return: total count of the events elements elements in the BPMNDiagramGraph instance
138 | """
139 |
140 | return sum([
141 | count for name, count in get_events_counts(bpmn_graph).items()
142 | ])
143 |
144 |
145 | def TNSE_metric(bpmn_graph):
146 | """
147 | Returns the value of the TNSE metric (Total Number of Start Events of the Model)
148 | for the BPMNDiagramGraph instance.
149 |
150 | :param bpmn_graph: an instance of BpmnDiagramGraph representing BPMN model.
151 | """
152 |
153 | return get_nodes_count(bpmn_graph, node_type='startEvent')
154 |
155 |
156 | def TNIE_metric(bpmn_graph):
157 | """
158 | Returns the value of the TNIE metric (Total Number of Intermediate Events of the Model)
159 | for the BPMNDiagramGraph instance.
160 |
161 | :param bpmn_graph: an instance of BpmnDiagramGraph representing BPMN model.
162 | """
163 |
164 | return get_nodes_count(bpmn_graph, node_type='intermediateCatchEvent') + \
165 | get_nodes_count(bpmn_graph, node_type='intermediateThrowEvent')
166 |
167 |
168 | def TNEE_metric(bpmn_graph):
169 | """
170 | Returns the value of the TNEE metric (Total Number of End Events of the Model)
171 | for the BPMNDiagramGraph instance.
172 |
173 | :param bpmn_graph: an instance of BpmnDiagramGraph representing BPMN model.
174 | """
175 |
176 | return get_nodes_count(bpmn_graph, node_type='endEvent')
177 |
178 |
179 | def TNE_metric(bpmn_graph):
180 | """
181 | Returns the value of the TNE metric (Total Number of Events of the Model)
182 | for the BPMNDiagramGraph instance.
183 |
184 | :param bpmn_graph: an instance of BpmnDiagramGraph representing BPMN model.
185 | """
186 |
187 | events_counts = get_events_counts(bpmn_graph)
188 |
189 | return sum(
190 | [count for _, count in events_counts.items()]
191 | )
192 |
193 |
194 | def NOA_metric(bpmn_graph):
195 | """
196 | Returns the value of the NOA metric (Number of Activities)
197 | for the BPMNDiagramGraph instance.
198 |
199 | :param bpmn_graph: an instance of BpmnDiagramGraph representing BPMN model.
200 | """
201 |
202 | activities_counts = get_activities_counts(bpmn_graph)
203 |
204 | return activities_counts["task"] + activities_counts["subProcess"]
205 |
206 |
207 | def NOAC_metric(bpmn_graph):
208 | """
209 | Returns the value of the NOAC metric (Number of Activities and control flow elements)
210 | for the BPMNDiagramGraph instance.
211 |
212 | :param bpmn_graph: an instance of BpmnDiagramGraph representing BPMN model.
213 | """
214 |
215 | activities_count = all_activities_count(bpmn_graph)
216 | control_flow_count = all_control_flow_elements_count(bpmn_graph)
217 |
218 | return activities_count + control_flow_count
219 |
220 |
221 | def NOAJS_metric(bpmn_graph):
222 | """
223 | Returns the value of the NOAJS metric (Number of Activities, joins and splits)
224 | for the BPMNDiagramGraph instance.
225 |
226 | :param bpmn_graph: an instance of BpmnDiagramGraph representing BPMN model.
227 | """
228 |
229 | activities_count = all_activities_count(bpmn_graph)
230 | gateways_count = all_gateways_count(bpmn_graph)
231 |
232 | return activities_count + gateways_count
233 |
234 |
235 | def NumberOfNodes_metric(bpmn_graph):
236 | """
237 | Returns the value of the Number of Nodes metric
238 | ("Number of activities and routing elements in a model")
239 | for the BPMNDiagramGraph instance.
240 |
241 | :param bpmn_graph: an instance of BpmnDiagramGraph representing BPMN model.
242 | """
243 |
244 | activities_count = all_activities_count(bpmn_graph)
245 | control_flow_count = all_control_flow_elements_count(bpmn_graph)
246 |
247 | return activities_count + control_flow_count
248 |
249 |
250 | def GatewayHeterogenity_metric(bpmn_graph):
251 | """
252 | Returns the value of the Gateway Heterogenity metric
253 | ("Number of different types of gateways used in a mode")
254 | for the BPMNDiagramGraph instance.
255 |
256 | :param bpmn_graph: an instance of BpmnDiagramGraph representing BPMN model.
257 | """
258 |
259 | gateway_counts = get_gateway_counts(bpmn_graph)
260 | present_gateways = [gateway_name
261 | for gateway_name, count in gateway_counts.items()
262 | if count > 0]
263 |
264 | return len(present_gateways)
265 |
266 |
267 | def CoefficientOfNetworkComplexity_metric(bpmn_graph):
268 | """
269 | Returns the value of the Coefficient of Network Complexity metric
270 | ("Ratio of the total number of arcs in a process model to its total number of nodes.")
271 | for the BPMNDiagramGraph instance.
272 |
273 | :param bpmn_graph: an instance of BpmnDiagramGraph representing BPMN model.
274 | """
275 |
276 | return float(len(bpmn_graph.get_flows())) / float(len(bpmn_graph.get_nodes()))
277 |
278 |
279 | def AverageGatewayDegree_metric(bpmn_graph):
280 | """
281 | Returns the value of the Average Gateway Degree metric
282 | ("Average of the number of both incoming and outgoing arcs of the gateway nodes in the process model")
283 | for the BPMNDiagramGraph instance.
284 |
285 | :param bpmn_graph: an instance of BpmnDiagramGraph representing BPMN model.
286 | """
287 |
288 | gateways_ids = [gateway[0] for gateway in get_all_gateways(bpmn_graph)]
289 | all_nodes_degrees = bpmn_graph.diagram_graph.degree()
290 | gateways_degree_values = [all_nodes_degrees[gateway_id] for gateway_id in gateways_ids]
291 |
292 | return float(sum(gateways_degree_values)) / float(len(gateways_degree_values))
293 |
294 |
295 | def DurfeeSquare_metric(bpmn_graph):
296 | """
297 | Returns the value of the Durfee Square metric
298 | ("Durfee Square equals d if there are d types of elements
299 | which occur at least d times in the model (each),
300 | and the other types of elements occur no more than d times (each)")
301 | for the BPMNDiagramGraph instance.
302 |
303 | :param bpmn_graph: an instance of BpmnDiagramGraph representing BPMN model.
304 | """
305 |
306 | all_types_count = Counter([node[1]['type'] for node in bpmn_graph.get_nodes() if node[1]['type']])
307 | length = len(all_types_count)
308 |
309 | histogram = [0] * (length + 1)
310 | for _, count in all_types_count.items():
311 | histogram[min(count, length)] += 1
312 |
313 | sum_ = 0
314 | for i, count in reversed(list(enumerate(histogram))):
315 | sum_ += count
316 | if sum_ >= i:
317 | return i
318 |
319 | return 0
320 |
321 |
322 | def PerfectSquare_metric(bpmn_graph):
323 | """
324 | Returns the value of the Perfect Square metric
325 | ("Given a set of element types ranked
326 | in decreasing order of the number of their instances,
327 | the PSM is the (unique) largest number
328 | such that the top p types occur(together)
329 | at least p2 times.")
330 | for the BPMNDiagramGraph instance.
331 |
332 | :param bpmn_graph: an instance of BpmnDiagramGraph representing BPMN model.
333 | """
334 |
335 | all_types_count = Counter([node[1]['type'] for node in bpmn_graph.get_nodes() if node[1]['type']])
336 | sorted_counts = [count for _, count in all_types_count.most_common()]
337 |
338 | potential_perfect_square = min(len(sorted_counts), int(sqrt(sum(sorted_counts))))
339 |
340 | for i in range(potential_perfect_square, 0, -1):
341 | if sum(sorted_counts[:potential_perfect_square]) >= potential_perfect_square * potential_perfect_square:
342 | return potential_perfect_square
343 | else:
344 | potential_perfect_square -= 1
345 |
346 | return 0
347 |
--------------------------------------------------------------------------------
/bpmn_python/bpmn_diagram_visualizer.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | BPMN diagram visualization methods
4 | """
5 | import matplotlib.pyplot as plt
6 | import networkx as nx
7 | import pydotplus
8 | import bpmn_python.bpmn_python_consts as consts
9 |
10 | from networkx.drawing.nx_pydot import write_dot
11 |
12 |
13 | def visualize_diagram(bpmn_diagram):
14 | """
15 | Shows a simple visualization of diagram
16 |
17 | :param bpmn_diagram: an instance of BPMNDiagramGraph class.
18 | """
19 | g = bpmn_diagram.diagram_graph
20 | pos = bpmn_diagram.get_nodes_positions()
21 | nx.draw_networkx_nodes(g, pos, node_shape='s', node_color='white',
22 | nodelist=bpmn_diagram.get_nodes_id_list_by_type(consts.Consts.task))
23 | nx.draw_networkx_nodes(g, pos, node_shape='s', node_color='white',
24 | nodelist=bpmn_diagram.get_nodes_id_list_by_type(consts.Consts.subprocess))
25 | nx.draw_networkx_nodes(g, pos, node_shape='d', node_color='white',
26 | nodelist=bpmn_diagram.get_nodes_id_list_by_type(consts.Consts.complex_gateway))
27 | nx.draw_networkx_nodes(g, pos, node_shape='o', node_color='white',
28 | nodelist=bpmn_diagram.get_nodes_id_list_by_type(consts.Consts.event_based_gateway))
29 | nx.draw_networkx_nodes(g, pos, node_shape='d', node_color='white',
30 | nodelist=bpmn_diagram.get_nodes_id_list_by_type(consts.Consts.inclusive_gateway))
31 | nx.draw_networkx_nodes(g, pos, node_shape='d', node_color='white',
32 | nodelist=bpmn_diagram.get_nodes_id_list_by_type(consts.Consts.exclusive_gateway))
33 | nx.draw_networkx_nodes(g, pos, node_shape='d', node_color='white',
34 | nodelist=bpmn_diagram.get_nodes_id_list_by_type(consts.Consts.parallel_gateway))
35 | nx.draw_networkx_nodes(g, pos, node_shape='o', node_color='white',
36 | nodelist=bpmn_diagram.get_nodes_id_list_by_type(consts.Consts.start_event))
37 | nx.draw_networkx_nodes(g, pos, node_shape='o', node_color='white',
38 | nodelist=bpmn_diagram.get_nodes_id_list_by_type(consts.Consts.intermediate_catch_event))
39 | nx.draw_networkx_nodes(g, pos, node_shape='o', node_color='white',
40 | nodelist=bpmn_diagram.get_nodes_id_list_by_type(consts.Consts.end_event))
41 | nx.draw_networkx_nodes(g, pos, node_shape='o', node_color='white',
42 | nodelist=bpmn_diagram.get_nodes_id_list_by_type(consts.Consts.intermediate_throw_event))
43 |
44 | node_labels = {}
45 | for node in g.nodes(data=True):
46 | node_labels[node[0]] = node[1].get(consts.Consts.node_name)
47 | nx.draw_networkx_labels(g, pos, node_labels)
48 |
49 | nx.draw_networkx_edges(g, pos)
50 |
51 | edge_labels = {}
52 | for edge in g.edges(data=True):
53 | edge_labels[(edge[0], edge[1])] = edge[2].get(consts.Consts.name)
54 | nx.draw_networkx_edge_labels(g, pos, edge_labels)
55 |
56 | plt.show()
57 |
58 |
59 | def bpmn_diagram_to_dot_file(bpmn_diagram, file_name):
60 | """
61 | Convert diagram graph to dot file
62 |
63 | :param bpmn_diagram: an instance of BPMNDiagramGraph class,
64 | :param file_name: name of generated file.
65 | """
66 | g = bpmn_diagram.diagram_graph
67 | write_dot(g, file_name + ".dot")
68 |
69 |
70 | def bpmn_diagram_to_png(bpmn_diagram, file_name):
71 | """
72 | Create a png picture for given diagram
73 |
74 | :param bpmn_diagram: an instance of BPMNDiagramGraph class,
75 | :param file_name: name of generated file.
76 | """
77 | g = bpmn_diagram.diagram_graph
78 | graph = pydotplus.Dot()
79 |
80 | for node in g.nodes(data=True):
81 |
82 | if node[1].get(consts.Consts.type) == consts.Consts.task:
83 | n = pydotplus.Node(name=node[0], shape="box", style="rounded", label=node[1].get(consts.Consts.node_name))
84 | elif node[1].get(consts.Consts.type) == consts.Consts.exclusive_gateway:
85 | n = pydotplus.Node(name=node[0], shape="diamond", label=node[1].get(consts.Consts.node_name))
86 | else:
87 | n = pydotplus.Node(name=node[0], label=node[1].get(consts.Consts.node_name))
88 | graph.add_node(n)
89 |
90 | for edge in g.edges(data=True):
91 | e = pydotplus.Edge(src=edge[0], dst=edge[1], label=edge[2].get(consts.Consts.name))
92 | graph.add_edge(e)
93 |
94 | graph.write(file_name + ".png", format='png')
95 |
--------------------------------------------------------------------------------
/bpmn_python/bpmn_import_utils.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Class including utility method used in diagram importing
4 | """
5 |
6 | import bpmn_python.bpmn_python_consts as consts
7 |
8 |
9 | class BpmnImportUtils(object):
10 | """
11 | Class including utility method used in diagram importing
12 | """
13 |
14 | def __init__(self):
15 | pass
16 |
17 | @staticmethod
18 | def remove_namespace_from_tag_name(tag_name):
19 | """
20 | Helper function, removes namespace annotation from tag name.
21 |
22 | :param tag_name: string with tag name.
23 | """
24 | return tag_name.split(':')[-1]
25 |
26 | @staticmethod
27 | def iterate_elements(parent):
28 | """
29 | Helper function that iterates over child Nodes/Elements of parent Node/Element.
30 |
31 | :param parent: object of Element class, representing parent element.
32 | """
33 | element = parent.firstChild
34 | while element is not None:
35 | yield element
36 | element = element.nextSibling
37 |
38 | @staticmethod
39 | def generate_nodes_clasification(bpmn_diagram):
40 | """
41 | Diagram elements classification. Implementation based on article "A Simple Algorithm for Automatic Layout of
42 | BPMN Processes".
43 | Assigns a classification to the diagram element according to specific element parameters.
44 | - Element - every element of the process which is not an edge,
45 | - Start Event - all types of start events,
46 | - End Event - all types of end events,
47 | - Join - an element with more than one incoming edge,
48 | - Split - an element with more than one outgoing edge.
49 |
50 | :param bpmn_diagram: BPMNDiagramGraph class instance representing a BPMN process diagram.
51 | :return: a dictionary of classification labels. Key - node id. Values - a list of labels.
52 | """
53 | nodes_classification = {}
54 |
55 | classification_element = "Element"
56 | classification_start_event = "Start Event"
57 | classification_end_event = "End Event"
58 |
59 | task_list = bpmn_diagram.get_nodes(consts.Consts.task)
60 | for element in task_list:
61 | classification_labels = [classification_element]
62 | BpmnImportUtils.split_join_classification(element, classification_labels, nodes_classification)
63 |
64 | subprocess_list = bpmn_diagram.get_nodes(consts.Consts.subprocess)
65 | for element in subprocess_list:
66 | classification_labels = [classification_element]
67 | BpmnImportUtils.split_join_classification(element, classification_labels, nodes_classification)
68 |
69 | complex_gateway_list = bpmn_diagram.get_nodes(consts.Consts.complex_gateway)
70 | for element in complex_gateway_list:
71 | classification_labels = [classification_element]
72 | BpmnImportUtils.split_join_classification(element, classification_labels, nodes_classification)
73 |
74 | event_based_gateway_list = bpmn_diagram.get_nodes(consts.Consts.event_based_gateway)
75 | for element in event_based_gateway_list:
76 | classification_labels = [classification_element]
77 | BpmnImportUtils.split_join_classification(element, classification_labels, nodes_classification)
78 |
79 | inclusive_gateway_list = bpmn_diagram.get_nodes(consts.Consts.inclusive_gateway)
80 | for element in inclusive_gateway_list:
81 | classification_labels = [classification_element]
82 | BpmnImportUtils.split_join_classification(element, classification_labels, nodes_classification)
83 |
84 | exclusive_gateway_list = bpmn_diagram.get_nodes(consts.Consts.exclusive_gateway)
85 | for element in exclusive_gateway_list:
86 | classification_labels = [classification_element]
87 | BpmnImportUtils.split_join_classification(element, classification_labels, nodes_classification)
88 |
89 | parallel_gateway_list = bpmn_diagram.get_nodes(consts.Consts.parallel_gateway)
90 | for element in parallel_gateway_list:
91 | classification_labels = [classification_element]
92 | BpmnImportUtils.split_join_classification(element, classification_labels, nodes_classification)
93 |
94 | start_event_list = bpmn_diagram.get_nodes(consts.Consts.start_event)
95 | for element in start_event_list:
96 | classification_labels = [classification_element, classification_start_event]
97 | BpmnImportUtils.split_join_classification(element, classification_labels, nodes_classification)
98 |
99 | intermediate_catch_event_list = bpmn_diagram.get_nodes(consts.Consts.intermediate_catch_event)
100 | for element in intermediate_catch_event_list:
101 | classification_labels = [classification_element]
102 | BpmnImportUtils.split_join_classification(element, classification_labels, nodes_classification)
103 |
104 | end_event_list = bpmn_diagram.get_nodes(consts.Consts.end_event)
105 | for element in end_event_list:
106 | classification_labels = [classification_element, classification_end_event]
107 | BpmnImportUtils.split_join_classification(element, classification_labels, nodes_classification)
108 |
109 | intermediate_throw_event_list = bpmn_diagram.get_nodes(consts.Consts.intermediate_throw_event)
110 | for element in intermediate_throw_event_list:
111 | classification_labels = [classification_element]
112 | BpmnImportUtils.split_join_classification(element, classification_labels, nodes_classification)
113 |
114 | return nodes_classification
115 |
116 | @staticmethod
117 | def split_join_classification(element, classification_labels, nodes_classification):
118 | """
119 | Add the "Split", "Join" classification, if the element qualifies for.
120 |
121 | :param element: an element from BPMN diagram,
122 | :param classification_labels: list of labels attached to the element,
123 | :param nodes_classification: dictionary of classification labels. Key - node id. Value - a list of labels.
124 | """
125 | classification_join = "Join"
126 | classification_split = "Split"
127 | if len(element[1][consts.Consts.incoming_flow]) >= 2:
128 | classification_labels.append(classification_join)
129 | if len(element[1][consts.Consts.outgoing_flow]) >= 2:
130 | classification_labels.append(classification_split)
131 | nodes_classification[element[0]] = classification_labels
132 |
--------------------------------------------------------------------------------
/bpmn_python/bpmn_python_consts.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Package used to unify the different constant values used in entire project
4 | """
5 |
6 |
7 | class Consts(object):
8 | """
9 | Class used to unify the different constant values used in entire project
10 | """
11 | # BPMN 2.0 element attribute names
12 | id = "id"
13 | name = "name"
14 | # Flow nodes cannot use "name" parameter in dictionary, due to the errors with PyDot
15 | node_name = "node_name"
16 | gateway_direction = "gatewayDirection"
17 | default = "default"
18 | instantiate = "instantiate"
19 | event_gateway_type = "eventGatewayType"
20 | source_ref = "sourceRef"
21 | target_ref = "targetRef"
22 | triggered_by_event = "triggeredByEvent"
23 | parallel_multiple = "parallelMultiple"
24 | cancel_activity = "cancelActivity"
25 | attached_to_ref = "attachedToRef"
26 | is_interrupting = "isInterrupting"
27 | is_closed = "isClosed"
28 | is_executable = "isExecutable"
29 | is_expanded = "isExpanded"
30 | is_horizontal = "isHorizontal"
31 | is_collection = "isCollection"
32 | process_type = "processType"
33 | sequence_flow = "sequenceFlow"
34 | condition_expression = "conditionExpression"
35 | message_flow = "messageFlow"
36 | message_flows = "messageFlows"
37 |
38 | # CSV literals
39 | csv_order = "Order"
40 | csv_activity = "Activity"
41 | csv_condition = "Condition"
42 | csv_who = "Who"
43 | csv_subprocess = "Subprocess"
44 | csv_terminated = "Terminated"
45 |
46 | # BPMN 2.0 diagram interchange element attribute names
47 | bpmn_element = "bpmnElement"
48 | height = "height"
49 | width = "width"
50 | x = "x"
51 | y = "y"
52 |
53 | # BPMN 2.0 element names
54 | definitions = "definitions"
55 | collaboration = "collaboration"
56 | participant = "participant"
57 | participants = "participants"
58 | process = "process"
59 | process_ref = "processRef"
60 | lane = "lane"
61 | lanes = "lanes"
62 | lane_set = "laneSet"
63 | child_lane_set = "childLaneSet"
64 | flow_node_ref = "flowNodeRef"
65 | flow_node_refs = "flowNodeRefs"
66 | task = "task"
67 | user_task = "userTask"
68 | service_task = "serviceTask"
69 | manual_task = "manualTask"
70 | subprocess = "subProcess"
71 | data_object = "dataObject"
72 | complex_gateway = "complexGateway"
73 | event_based_gateway = "eventBasedGateway"
74 | inclusive_gateway = "inclusiveGateway"
75 | exclusive_gateway = "exclusiveGateway"
76 | parallel_gateway = "parallelGateway"
77 | start_event = "startEvent"
78 | intermediate_catch_event = "intermediateCatchEvent"
79 | end_event = "endEvent"
80 | intermediate_throw_event = "intermediateThrowEvent"
81 | boundary_event = "boundaryEvent"
82 |
83 | # BPMN 2.0 diagram interchange element names
84 | bpmn_shape = "BPMNShape"
85 | bpmn_edge = "BPMNEdge"
86 |
87 | # BPMN 2.0 child element names
88 | incoming_flow = "incoming"
89 | incoming_flow_list = "incoming_flow_list"
90 | outgoing_flow = "outgoing"
91 | outgoing_flow_list = "outgoing_flow_list"
92 | waypoint = "waypoint"
93 | waypoints = "waypoints"
94 |
95 | # Additional parameter names
96 | type = "type"
97 | event_definitions = "event_definitions"
98 | node_ids = "node_ids"
99 | definition_type = "definition_type"
100 |
101 | grid_column_width = 2
102 |
--------------------------------------------------------------------------------
/bpmn_python/diagram_layout_metrics.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Collection of different metrics used to compare diagram layout quality
4 | """
5 | import copy
6 | import bpmn_python.bpmn_python_consts as consts
7 |
8 |
9 | def count_crossing_points(bpmn_graph):
10 | """
11 |
12 | :param bpmn_graph:
13 | :return:
14 | """
15 | flows = bpmn_graph.get_flows()
16 | segments = get_flows_segments(flows)
17 |
18 | crossing_point_num = 0
19 | while segments:
20 | segment_one = segments.pop()
21 | for segment_two in segments:
22 | if segments_common_points(segment_one, segment_two) is False and do_intersect(segment_one, segment_two):
23 | crossing_point_num += 1
24 |
25 | return crossing_point_num
26 |
27 |
28 | def compute_determinant(p1, p2, p3):
29 | """
30 |
31 | :param p1:
32 | :param p2:
33 | :param p3:
34 | :return:
35 | """
36 | det = float(p1[0]) * float(p2[1]) + float(p2[0]) * float(p3[1]) + float(p3[0]) * float(p1[1])
37 | det -= float(p1[0]) * float(p3[1]) + float(p2[0]) * float(p1[1]) + float(p3[0]) * float(p2[1])
38 | return det
39 |
40 |
41 | def check_integer_sign(value):
42 | """
43 |
44 | :param value:
45 | :return:
46 | """
47 | return value >= 0
48 |
49 |
50 | def get_flows_segments(flows):
51 | """
52 |
53 | :param flows:
54 | """
55 | source_param_name = "source"
56 | target_param_name = "target"
57 |
58 | segments = []
59 | for flow in flows:
60 | waypoints = copy.deepcopy(flow[2][consts.Consts.waypoints])
61 | source = waypoints.pop(0)
62 | while len(waypoints) > 0:
63 | target = waypoints.pop(0)
64 | segments.append({source_param_name: {consts.Consts.x: float(source[0]), consts.Consts.y: float(source[1])},
65 | target_param_name: {consts.Consts.x: float(target[0]), consts.Consts.y: float(target[1])}})
66 | source = target
67 | return segments
68 |
69 |
70 | def segments_common_points(segment_one, segment_two):
71 | """
72 |
73 | :param segment_one:
74 | :param segment_two:
75 | :return:
76 | """
77 | source_param = "source"
78 | target_param = "target"
79 | return points_are_equal(segment_one[source_param], segment_two[source_param]) \
80 | or points_are_equal(segment_one[source_param], segment_two[target_param]) \
81 | or points_are_equal(segment_one[target_param], segment_two[source_param]) \
82 | or points_are_equal(segment_one[target_param], segment_two[target_param])
83 |
84 |
85 | def points_are_equal(p1, p2):
86 | """
87 |
88 | :param p1:
89 | :param p2:
90 | :return:
91 | """
92 | return p1[consts.Consts.x] == p2[consts.Consts.x] and p1[consts.Consts.y] == p2[consts.Consts.y]
93 |
94 |
95 | def do_intersect(segment_one, segment_two):
96 | """
97 |
98 | :param segment_one:
99 | :param segment_two:
100 | :return:
101 | """
102 | source_param = "source"
103 | target_param = "target"
104 | # Find the four orientations needed for general and special cases
105 | o1 = orientation(segment_one[source_param], segment_one[target_param], segment_two[source_param])
106 | o2 = orientation(segment_one[source_param], segment_one[target_param], segment_two[target_param])
107 | o3 = orientation(segment_two[source_param], segment_two[target_param], segment_one[source_param])
108 | o4 = orientation(segment_two[source_param], segment_two[target_param], segment_one[target_param])
109 |
110 | if o1 != o2 and o3 != o4:
111 | return True
112 |
113 | # Special Cases
114 | if o1 == 0 and lies_on_segment(segment_one[source_param], segment_one[target_param], segment_two[source_param]):
115 | return True
116 |
117 | if o2 == 0 and lies_on_segment(segment_one[source_param], segment_one[target_param], segment_two[target_param]):
118 | return True
119 |
120 | if o3 == 0 and lies_on_segment(segment_two[source_param], segment_two[target_param], segment_one[source_param]):
121 | return True
122 |
123 | if o4 == 0 and lies_on_segment(segment_two[source_param], segment_two[target_param], segment_one[target_param]):
124 | return True
125 |
126 | # Neither of special cases
127 | return False
128 |
129 |
130 | def orientation(p1, p2, p3):
131 | """
132 | Finds orientation of three points p1, p2, p3.
133 | The function returns following values
134 | 0 --> p1, p2 and p3 are collinear
135 | 1 --> Clockwise
136 | 2 --> Counterclockwise
137 | :param p1: tuple representing two dimensional point
138 | :param p2: tuple representing two dimensional point
139 | :param p3: tuple representing two dimensional point
140 | """
141 | val = (p2[consts.Consts.y] - p1[consts.Consts.y]) * (p3[consts.Consts.x] - p2[consts.Consts.x]) \
142 | - (p2[consts.Consts.x] - p1[consts.Consts.x]) * (p3[consts.Consts.y] - p2[consts.Consts.y])
143 |
144 | if val == 0:
145 | return 0 # collinear
146 | elif val > 0:
147 | return 1 # clockwise
148 | else:
149 | return 2 # counterclockwise
150 |
151 |
152 | def lies_on_segment(p1, p2, p3):
153 | """
154 |
155 | :param p1:
156 | :param p2:
157 | :param p3:
158 | :return:
159 | """
160 | return min(p1[consts.Consts.x], p2[consts.Consts.x]) <= p3[consts.Consts.x] \
161 | <= max(p1[consts.Consts.x], p2[consts.Consts.x])\
162 | and min(p1[consts.Consts.y], p2[consts.Consts.y]) <= p3[consts.Consts.y] \
163 | <= max(p1[consts.Consts.y], p2[consts.Consts.y])
164 |
165 |
166 | def count_segments(bpmn_graph):
167 | """
168 |
169 | :param bpmn_graph:
170 | """
171 | flows = bpmn_graph.get_flows()
172 | segments = get_flows_segments(flows)
173 | return len(segments)
174 |
175 |
176 | def compute_longest_path(bpmn_graph):
177 | """
178 |
179 | :param bpmn_graph:
180 | """
181 | incoming_flows_list_param_name = "incoming"
182 |
183 | nodes = copy.deepcopy(bpmn_graph.get_nodes())
184 | no_incoming_flow_nodes = []
185 | for node in nodes:
186 | incoming_list = node[1][incoming_flows_list_param_name]
187 | if len(incoming_list) == 0:
188 | no_incoming_flow_nodes.append(node)
189 |
190 | longest_path = []
191 | for node in no_incoming_flow_nodes:
192 | (output_path, output_path_len) = find_longest_path([], node, bpmn_graph)
193 | if output_path_len > len(longest_path):
194 | longest_path = output_path
195 | return longest_path, len(longest_path)
196 |
197 |
198 | def find_longest_path(previous_nodes, node, bpmn_graph):
199 | """
200 |
201 | :param previous_nodes:
202 | :param node:
203 | :param bpmn_graph:
204 | :return:
205 | """
206 | outgoing_flows_list_param_name = "outgoing"
207 | outgoing_flows_list = node[1][outgoing_flows_list_param_name]
208 | longest_path = []
209 |
210 | if len(outgoing_flows_list) == 0:
211 | tmp_previous_nodes = copy.deepcopy(previous_nodes)
212 | tmp_previous_nodes.append(node)
213 | return tmp_previous_nodes, len(tmp_previous_nodes)
214 | else:
215 | tmp_previous_nodes = copy.deepcopy(previous_nodes)
216 | tmp_previous_nodes.append(node)
217 | for outgoing_flow_id in outgoing_flows_list:
218 | flow = bpmn_graph.get_flow_by_id(outgoing_flow_id)
219 | outgoing_node = bpmn_graph.get_node_by_id(flow[2][consts.Consts.target_ref])
220 | if outgoing_node not in previous_nodes:
221 | (output_path, output_path_len) = find_longest_path(tmp_previous_nodes, outgoing_node, bpmn_graph)
222 | if output_path_len > len(longest_path):
223 | longest_path = output_path
224 | return longest_path, len(longest_path)
225 |
226 |
227 | def compute_longest_path_tasks(bpmn_graph):
228 | """
229 |
230 | :param bpmn_graph:
231 | """
232 | incoming_flows_list_param_name = "incoming"
233 |
234 | nodes = copy.deepcopy(bpmn_graph.get_nodes())
235 | no_incoming_flow_nodes = []
236 | for node in nodes:
237 | incoming_list = node[1][incoming_flows_list_param_name]
238 | if len(incoming_list) == 0:
239 | no_incoming_flow_nodes.append(node)
240 |
241 | longest_path = []
242 | for node in no_incoming_flow_nodes:
243 | (all_nodes, qualified_nodes) = find_longest_path_tasks([], [], node, bpmn_graph)
244 | if len(qualified_nodes) > len(longest_path):
245 | longest_path = qualified_nodes
246 | return longest_path, len(longest_path)
247 |
248 |
249 | def find_longest_path_tasks(path, qualified_nodes, node, bpmn_graph):
250 | """
251 |
252 | :param path:
253 | :param qualified_nodes:
254 | :param node:
255 | :param bpmn_graph:
256 | :return:
257 | """
258 | node_names = {"task", "subProcess"}
259 | outgoing_flows_list = node[1][consts.Consts.outgoing_flow]
260 |
261 | if len(outgoing_flows_list) == 0:
262 | tmp_path = copy.deepcopy(path)
263 | tmp_path.append(node)
264 | tmp_qualified_nodes = copy.deepcopy(qualified_nodes)
265 | if node[1][consts.Consts.type] in node_names:
266 | tmp_qualified_nodes.append(node)
267 | return tmp_path, tmp_qualified_nodes
268 | else:
269 | longest_qualified_nodes = []
270 | longest_path = copy.deepcopy(path)
271 | longest_path.append(node)
272 | for outgoing_flow_id in outgoing_flows_list:
273 | flow = bpmn_graph.get_flow_by_id(outgoing_flow_id)
274 | outgoing_node = bpmn_graph.get_node_by_id(flow[2][consts.Consts.target_ref])
275 | tmp_path = copy.deepcopy(path)
276 | tmp_path.append(node)
277 | tmp_qualified_nodes = copy.deepcopy(qualified_nodes)
278 | if node[1]["type"] in node_names:
279 | tmp_qualified_nodes.append(node)
280 |
281 | if outgoing_node not in path:
282 | (path_all_nodes, path_qualified_nodes) = find_longest_path_tasks(tmp_path, tmp_qualified_nodes,
283 | outgoing_node, bpmn_graph)
284 | if len(path_qualified_nodes) > len(longest_qualified_nodes):
285 | longest_qualified_nodes = path_qualified_nodes
286 | longest_path = path_all_nodes
287 | else:
288 | if len(tmp_qualified_nodes) > len(longest_qualified_nodes):
289 | longest_qualified_nodes = tmp_qualified_nodes
290 | longest_path = tmp_path
291 | return longest_path, longest_qualified_nodes
292 |
--------------------------------------------------------------------------------
/bpmn_python/graph/__init__.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Package init file
4 | """
5 | __all__ = ["classes"]
6 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/__init__.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Package init file
4 | """
5 | __all__ = ["activities", "events", "gateways", "root_element", "base_element_type", "condition_expression_type",
6 | "flow_element_type", "flow_node_type", "lane_set_type", "lane_type", "message_flow_type", "participant_type",
7 | "sequence_flow_type"]
8 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/activities/__init__.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Package init file
4 | """
5 | __all__ = ["activity_type", "subprocess_type", "task_type"]
6 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/activities/activity_type.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Class used for representing tActivity of BPMN 2.0 graph
4 | """
5 | import graph.classes.flow_node_type as flow_node
6 |
7 |
8 | class Activity(flow_node.FlowNode):
9 | """
10 | Class used for representing tActivity of BPMN 2.0 graph
11 | Fields (except inherited):
12 | - default: ID of default flow of gateway. Must be either None (default is optional according to BPMN 2.0 XML Schema)
13 | or String.
14 | """
15 |
16 | def __init__(self):
17 | """
18 | Default constructor, initializes object fields with new instances.
19 | """
20 | super(Activity, self).__init__()
21 | self.__default = None
22 |
23 | def get_default(self):
24 | """
25 | Getter for 'default' field.
26 | :return:a value of 'default' field.
27 | """
28 | return self.__default
29 |
30 | def set_default(self, value):
31 | """
32 | Setter for 'default' field.
33 | :param value - a new value of 'default' field. Must be either None (default is optional according to
34 | BPMN 2.0 XML Schema) or String.
35 | """
36 | if value is None:
37 | self.__default = value
38 | elif not isinstance(value, str):
39 | raise TypeError("Default must be set to a String")
40 | else:
41 | self.__default = value
42 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/activities/subprocess_type.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Class used for representing tSubProcess of BPMN 2.0 graph
4 | """
5 | import graph.classes.activities.activity_type as activity
6 | import graph.classes.flow_element_type as flow_element
7 | import graph.classes.lane_set_type as lane_set
8 |
9 |
10 | class SubProcess(activity.Activity):
11 | """
12 | Class used for representing tSubProcess of BPMN 2.0 graph
13 | Fields (except inherited):
14 | - lane_set_list: a list of LaneSet objects.
15 | - flow_element_list: a list of FlowElement objects.
16 | - triggered_by_event: a boolean value..
17 | """
18 |
19 | def __init__(self):
20 | """
21 | Default constructor, initializes object fields with new instances.
22 | """
23 | super(SubProcess, self).__init__()
24 | self.__triggered_by_event = False
25 | self.__lane_set_list = []
26 | self.__flow_element_list = []
27 |
28 | def triggered_by_event(self):
29 | """
30 | Getter for 'triggered_by_event' field.
31 | :return: value of 'triggered_by_event' field.
32 | """
33 | return self.__triggered_by_event
34 |
35 | def set_triggered_by_event(self, value):
36 | """
37 | Setter for 'triggered_by_event' field.
38 | :param value - a new value of 'triggered_by_event' field. Must be a boolean type. Does not accept None value.
39 | """
40 | if value is None or not isinstance(value, bool):
41 | raise TypeError("TriggeredByEvent must be set to a bool")
42 | else:
43 | self.__triggered_by_event = value
44 |
45 | def get_lane_set_list(self):
46 | """
47 | Getter for 'lane_set_list' field.
48 | :return:a value of 'lane_set_list' field.
49 | """
50 | return self.__lane_set_list
51 |
52 | def set_lane_set_list(self, value):
53 | """
54 | Setter for 'lane_set_list' field.
55 | :param value - a new value of 'lane_set_list' field. Must be a list
56 | """
57 | if value is None or not isinstance(value, list):
58 | raise TypeError("LaneSetList new value must be a list")
59 | else:
60 | for element in value:
61 | if not isinstance(element, lane_set.LaneSet):
62 | raise TypeError("LaneSetList elements in variable must be of LaneSet class")
63 | self.__lane_set_list = value
64 |
65 | def get_flow_element_list(self):
66 | """
67 | Getter for 'flow_element_list' field.
68 | :return:a value of 'flow_element_list' field.
69 | """
70 | return self.__flow_element_list
71 |
72 | def set_flow_element_list(self, value):
73 | """
74 | Setter for 'flow_element_list' field.
75 | :param value - a new value of 'flow_element_list' field. Must be a list
76 | """
77 | if value is None or not isinstance(value, list):
78 | raise TypeError("FlowElementList new value must be a list")
79 | else:
80 | for element in value:
81 | if not isinstance(element, flow_element.FlowElement):
82 | raise TypeError("FlowElementList elements in variable must be of FlowElement class")
83 | self.__flow_element_list = value
84 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/activities/task_type.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Class used for representing tTask of BPMN 2.0 graph
4 | """
5 | import graph.classes.activities.activity_type as activity
6 |
7 |
8 | class Task(activity.Activity):
9 | """
10 | Class used for representing tTask of BPMN 2.0 graph
11 | """
12 |
13 | def __init__(self):
14 | """
15 | Default constructor, initializes object fields with new instances.
16 | """
17 | super(Task, self).__init__()
18 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/base_element_type.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Class used for representing tBaseElement of BPMN 2.0 graph
4 | """
5 |
6 |
7 | class BaseElement(object):
8 | """
9 | Class used for representing tBaseElement of BPMN 2.0 graph.
10 | Fields:
11 | - id: an ID of element. Must be either None (ID is optional according to BPMN 2.0 XML Schema) or String.
12 | """
13 |
14 | def __init__(self):
15 | """
16 | Default constructor, initializes object fields with new instances.
17 | """
18 | self.__id = None
19 |
20 | def get_id(self):
21 | """
22 | Getter for 'id' field.
23 | :return: value of 'id' field.
24 | """
25 | return self.__id
26 |
27 | def set_id(self, value):
28 | """
29 | Setter for 'id' field.
30 | :param value - a new value of 'id' field. Must be either None (ID is optional according to BPMN 2.0 XML Schema)
31 | or String type.
32 | """
33 | if value is None:
34 | self.__id = value
35 | if not isinstance(value, str):
36 | raise TypeError("ID must be set to a String")
37 | else:
38 | self.__id = value
39 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/condition_expression_type.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Class used for representing condition expression in sequence flow
4 | """
5 |
6 |
7 | class ConditionExpression(object):
8 | """
9 | Class used for representing condition expression in sequence flow
10 | Fields:
11 | - condition: condition expression. Required field. Must be a String.
12 | """
13 |
14 | def __init__(self):
15 | """
16 | Default constructor, initializes object fields with new instances.
17 | """
18 | super(ConditionExpression, self).__init__()
19 | self.__condition = None
20 |
21 | def get_condition(self):
22 | """
23 | Getter for 'condition' field.
24 | :return:a value of 'condition' field.
25 | """
26 | return self.__condition
27 |
28 | def set_condition(self, value):
29 | """
30 | Setter for 'condition' field.
31 | :param value - a new value of 'condition' field. Required field. Must be a String.
32 | """
33 | if value is None or not isinstance(value, str):
34 | raise TypeError("Condition is required and must be set to a String")
35 | else:
36 | self.__condition = value
37 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/events/__init__.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Package init file
4 | """
5 | __all__ = ["catch_event_type", "end_event_type", "event_type", "intermediate_catch_event_type",
6 | "intermediate_throw_event_type", "start_event_type", "throw_event_type"]
7 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/events/catch_event_type.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Class used for representing tCatchEvent of BPMN 2.0 graph
4 | """
5 | import graph.classes.events.event_type as event
6 | import graph.classes.root_element.event_definition_type as event_definition
7 |
8 |
9 | class CatchEvent(event.Event):
10 | """
11 | Class used for representing tCatchEvent of BPMN 2.0 graph
12 | Fields (except inherited):
13 | - parallel_multiple: a boolean value. default value "false".
14 | - event_definition_list: a list of EventDefinition objects. Optional value.
15 | """
16 |
17 | def __init__(self):
18 | """
19 | Default constructor, initializes object fields with new instances.
20 | """
21 | super(CatchEvent, self).__init__()
22 | self.__parallel_multiple = False
23 | self.__event_definition_list = []
24 |
25 | def parallel_multiple(self):
26 | """
27 | Getter for 'parallel_multiple' field.
28 | :return: value of 'parallel_multiple' field.
29 | """
30 | return self.__parallel_multiple
31 |
32 | def set_parallel_multiple(self, value):
33 | """
34 | Setter for 'parallel_multiple' field.
35 | :param value - a new value of 'parallel_multiple' field. Must be a boolean type. Does not accept None value.
36 | """
37 | if value is None or not isinstance(value, bool):
38 | raise TypeError("ParallelMultiple must be set to a bool")
39 | else:
40 | self.__parallel_multiple = value
41 |
42 | def get_event_definition_list(self):
43 | """
44 | Getter for 'event_definition_list' field.
45 | :return: value of 'event_definition_list' field.
46 | """
47 | return self.__event_definition_list
48 |
49 | def set_event_definition_list(self, value):
50 | """
51 | Setter for 'event_definition_list' field.
52 | :param value - a new value of 'event_definition_list' field. Must be a list of EventDefinition objects
53 | """
54 | if value is None or not isinstance(value, list):
55 | raise TypeError("EventDefinitionList new value must be a list")
56 | else:
57 | for element in value:
58 | if not isinstance(element, event_definition.EventDefinition):
59 | raise TypeError("EventDefinitionList elements in variable must be of Lane class")
60 | self.__event_definition_list = value
61 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/events/end_event_type.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Class used for representing tEndEvent of BPMN 2.0 graph
4 | """
5 | import graph.classes.events.throw_event_type as throw_event
6 |
7 |
8 | class EndEvent(throw_event.ThrowEvent):
9 | """
10 | Class used for representing tEndEvent of BPMN 2.0 graph
11 | """
12 |
13 | def __init__(self):
14 | """
15 | Default constructor, initializes object fields with new instances.
16 | """
17 | super(EndEvent, self).__init__()
18 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/events/event_type.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Class used for representing tEvent of BPMN 2.0 graph
4 | """
5 | import graph.classes.flow_node_type as flow_node
6 |
7 |
8 | class Event(flow_node.FlowNode):
9 | """
10 | Class used for representing tEvent of BPMN 2.0 graph
11 | """
12 |
13 | def __init__(self):
14 | """
15 | Default constructor, initializes object fields with new instances.
16 | """
17 | super(Event, self).__init__()
18 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/events/intermediate_catch_event_type.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Class used for representing tIntermediateCatchEvent of BPMN 2.0 graph
4 | """
5 | import graph.classes.events.catch_event_type as catch_event
6 |
7 |
8 | class IntermediateCatchEvent(catch_event.CatchEvent):
9 | """
10 | Class used for representing tIntermediateCatchEvent of BPMN 2.0 graph
11 | """
12 |
13 | def __init__(self):
14 | """
15 | Default constructor, initializes object fields with new instances.
16 | """
17 | super(IntermediateCatchEvent, self).__init__()
18 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/events/intermediate_throw_event_type.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Class used for representing tIntermediateThrowEvent of BPMN 2.0 graph
4 | """
5 | import graph.classes.events.throw_event_type as throw_event
6 |
7 |
8 | class IntermediateThrowEvent(throw_event.ThrowEvent):
9 | """
10 | Class used for representing tIntermediateThrowEvent of BPMN 2.0 graph
11 | """
12 |
13 | def __init__(self):
14 | """
15 | Default constructor, initializes object fields with new instances.
16 | """
17 | super(IntermediateThrowEvent, self).__init__()
18 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/events/start_event_type.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Class used for representing tStartEvent of BPMN 2.0 graph
4 | """
5 | import graph.classes.events.catch_event_type as catch_event
6 |
7 |
8 | class StartEvent(catch_event.CatchEvent):
9 | """
10 | Class used for representing tStartEvent of BPMN 2.0 graph
11 | """
12 |
13 | def __init__(self):
14 | """
15 | Default constructor, initializes object fields with new instances.
16 | """
17 | super(StartEvent, self).__init__()
18 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/events/throw_event_type.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Class used for representing tThrowEvent of BPMN 2.0 graph
4 | """
5 | import graph.classes.events.event_type as event
6 | import graph.classes.root_element.event_definition_type as event_definition
7 |
8 |
9 | class ThrowEvent(event.Event):
10 | """
11 | Class used for representing tThrowEvent of BPMN 2.0 graph
12 | Fields (except inherited):
13 | - event_definition_list: a list of EventDefinition objects. Optional value.
14 | """
15 |
16 | def __init__(self):
17 | """
18 | Default constructor, initializes object fields with new instances.
19 | """
20 | super(ThrowEvent, self).__init__()
21 | self.__event_definition_list = []
22 |
23 | def get_event_definition_list(self):
24 | """
25 | Getter for 'event_definition_list' field.
26 | :return: value of 'event_definition_list' field.
27 | """
28 | return self.__event_definition_list
29 |
30 | def set_event_definition_list(self, value):
31 | """
32 | Setter for 'event_definition_list' field.
33 | :param value - a new value of 'event_definition_list' field. Must be a list of EventDefinition objects
34 | """
35 | if value is None or not isinstance(value, list):
36 | raise TypeError("EventDefinitionList new value must be a list")
37 | else:
38 | for element in value:
39 | if not isinstance(element, event_definition.EventDefinition):
40 | raise TypeError("EventDefinitionList elements in variable must be of Lane class")
41 | self.__event_definition_list = value
42 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/flow_element_type.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Class used for representing tFlowElement of BPMN 2.0 graph
4 | """
5 | import graph.classes.base_element_type as base_element
6 |
7 |
8 | class FlowElement(base_element.BaseElement):
9 | """
10 | Class used for representing tFlowElement of BPMN 2.0 graph.
11 | Fields (except inherited):
12 | - name: name of element. Must be either None (name is optional according to BPMN 2.0 XML Schema) or String.
13 | """
14 |
15 | def __init__(self):
16 | """
17 | Default constructor, initializes object fields with new instances.
18 | """
19 | super(FlowElement, self).__init__()
20 | self.__name = None
21 |
22 | def get_name(self):
23 | """
24 | Getter for 'name' field.
25 | :return:a value of 'name' field.
26 | """
27 | return self.__name
28 |
29 | def set_name(self, value):
30 | """
31 | Setter for 'name' field.
32 | :param value - a new value of 'name' field. Must be either None (name is optional according to BPMN 2.0 XML
33 | Schema) or String.
34 | """
35 | if value is None:
36 | self.__name = value
37 | elif not isinstance(value, str):
38 | raise TypeError("Name must be set to a String")
39 | else:
40 | self.__name = value
41 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/flow_node_type.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Class used for representing tFlowNode of BPMN 2.0 graph
4 | """
5 | import bpmn_python.graph.classes.flow_element_type as flow_element_type
6 |
7 |
8 | class FlowNode(flow_element_type.FlowElement):
9 | """
10 | Class used for representing tFlowNode of BPMN 2.0 graph
11 | """
12 |
13 | def __init__(self):
14 | """
15 | Default constructor, initializes object fields with new instances.
16 | Fields (except inherited):
17 | - incoming_list: a list of IDs (String type) of incoming flows.
18 | - outgoing_list: a list of IDs (String type) of outgoing flows.
19 | """
20 | super(FlowNode, self).__init__()
21 | self.__incoming_list = []
22 | self.__outgoing_list = []
23 |
24 | def get_incoming(self):
25 | """
26 | Getter for 'incoming' field.
27 | :return:a value of 'incoming' field.
28 | """
29 | return self.__incoming_list
30 |
31 | def set_incoming(self, value):
32 | """
33 | Setter for 'incoming' field.
34 | :param value - a new value of 'incoming' field. List of IDs (String type) of incoming flows.
35 | """
36 | if not isinstance(value, list):
37 | raise TypeError("IncomingList new value must be a list")
38 | for element in value:
39 | if not isinstance(element, str):
40 | raise TypeError("IncomingList elements in variable must be of String class")
41 | self.__incoming_list = value
42 |
43 | def get_outgoing(self):
44 | """
45 | Getter for 'outgoing' field.
46 | :return:a value of 'outgoing' field.
47 | """
48 | return self.__outgoing_list
49 |
50 | def set_outgoing(self, value):
51 | """
52 | Setter for 'outgoing' field.
53 | :param value - a new value of 'outgoing' field. Must be a list of IDs (String type) of outgoing flows.
54 | """
55 | if not isinstance(value, list):
56 | raise TypeError("OutgoingList new value must be a list")
57 | for element in value:
58 | if not isinstance(element, str):
59 | raise TypeError("OutgoingList elements in variable must be of String class")
60 | self.__outgoing_list = value
61 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/gateways/__init__.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Package init file
4 | """
5 | __all__ = ["exclusive_gateway_type", "gateway_type", "inclusive_gateway_type", "parallel_gateway_type"]
6 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/gateways/exclusive_gateway_type.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Class used for representing tInclusiveGateway of BPMN 2.0 graph
4 | """
5 | import graph.classes.gateways.gateway_type as gateway
6 |
7 |
8 | class ExclusiveGateway(gateway.Gateway):
9 | """
10 | Class used for representing tExclusiveGateway of BPMN 2.0 graph
11 | Fields (except inherited):
12 | - default: ID of default flow of gateway. Must be either None (default is optional according to BPMN 2.0 XML Schema)
13 | or String.
14 | """
15 |
16 | def __init__(self):
17 | """
18 | Default constructor, initializes object fields with new instances.
19 | """
20 | super(ExclusiveGateway, self).__init__()
21 | self.__default = None
22 |
23 | def get_default(self):
24 | """
25 | Getter for 'default' field.
26 | :return:a value of 'default' field.
27 | """
28 | return self.__default
29 |
30 | def set_default(self, value):
31 | """
32 | Setter for 'default' field.
33 | :param value - a new value of 'default' field. Must be either None (default is optional according to
34 | BPMN 2.0 XML Schema) or String.
35 | """
36 | if value is None:
37 | self.__default = value
38 | elif not isinstance(value, str):
39 | raise TypeError("Default must be set to a String")
40 | else:
41 | self.__default = value
42 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/gateways/gateway_type.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Class used for representing tGateway of BPMN 2.0 graph
4 | """
5 | import graph.classes.flow_node_type as flow_node
6 |
7 |
8 | class Gateway(flow_node.FlowNode):
9 | """
10 | Class used for representing tGateway of BPMN 2.0 graph
11 | """
12 | __gateway_directions_list = ["Unspecified", "Converging", "Diverging", "Mixed"]
13 |
14 | def __init__(self):
15 | """
16 | Default constructor, initializes object fields with new instances.
17 | """
18 | super(Gateway, self).__init__()
19 | self.__gateway_direction = "Unspecified"
20 |
21 | def get_gateway_direction(self):
22 | """
23 | Getter for 'gateway_direction' field.
24 | :return:a value of 'gateway_direction' field.
25 | """
26 | return self.__gateway_direction
27 |
28 | def set_gateway_direction(self, value):
29 | """
30 | Setter for 'gateway_direction' field.
31 | :param value - a new value of 'gateway_direction' field.
32 | """
33 | if value is None or not isinstance(value, str):
34 | raise TypeError("GatewayDirection must be set to a String")
35 | elif value not in Gateway.__gateway_directions_list:
36 | raise ValueError("GatewayDirection must be one of specified values: 'Unspecified', 'Converging', "
37 | "'Diverging', 'Mixed'")
38 | else:
39 | self.__gateway_direction = value
40 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/gateways/inclusive_gateway_type.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Class used for representing tInclusiveGateway of BPMN 2.0 graph
4 | """
5 | import graph.classes.gateways.gateway_type as gateway
6 |
7 |
8 | class InclusiveGateway(gateway.Gateway):
9 | """
10 | Class used for representing tInclusiveGateway of BPMN 2.0 graph
11 | Fields (except inherited):
12 | - default: ID of default flow of gateway. Must be either None (default is optional according to BPMN 2.0 XML Schema)
13 | or String.
14 | """
15 |
16 | def __init__(self):
17 | """
18 | Default constructor, initializes object fields with new instances.
19 | """
20 | super(InclusiveGateway, self).__init__()
21 | self.__default = None
22 |
23 | def get_default(self):
24 | """
25 | Getter for 'default' field.
26 | :return:a value of 'default' field.
27 | """
28 | return self.__default
29 |
30 | def set_default(self, value):
31 | """
32 | Setter for 'default' field.
33 | :param value - a new value of 'default' field. Must be either None (default is optional according to
34 | BPMN 2.0 XML Schema) or String.
35 | """
36 | if value is None:
37 | self.__default = value
38 | elif not isinstance(value, str):
39 | raise TypeError("Default must be set to a String")
40 | else:
41 | self.__default = value
42 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/gateways/parallel_gateway_type.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Class used for representing tParallelGateway of BPMN 2.0 graph
4 | """
5 | import graph.classes.gateways.gateway_type as gateway
6 |
7 |
8 | class ParallelGateway(gateway.Gateway):
9 | """
10 | Class used for representing tParallelGateway of BPMN 2.0 graph
11 | """
12 |
13 | def __init__(self):
14 | """
15 | Default constructor, initializes object fields with new instances.
16 | """
17 | super(ParallelGateway, self).__init__()
18 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/lane_set_type.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Class used for representing tSetLane of BPMN 2.0 graph
4 | """
5 | import graph.classes.base_element_type as base_element
6 | import graph.classes.lane_type as lane
7 |
8 |
9 | class LaneSet(base_element.BaseElement):
10 | """
11 | Class used for representing tSetLane of BPMN 2.0 graph.
12 | Fields (except inherited):
13 | - name: name of element. Must be either None (name is optional according to BPMN 2.0 XML Schema) or String.
14 | - lane_list: a list of Lane objects.
15 | """
16 |
17 | def __init__(self):
18 | """
19 | Default constructor, initializes object fields with new instances.
20 | """
21 | super(LaneSet, self).__init__()
22 | self.__name = None
23 | self.__lane_list = []
24 |
25 | def get_name(self):
26 | """
27 | Getter for 'name' field.
28 | :return: value of 'name' field.
29 | """
30 | return self.__name
31 |
32 | def set_name(self, value):
33 | """
34 | Setter for 'name' field.
35 | :param value - a new value of 'name' field. Must be either None (name is optional according to BPMN 2.0 XML
36 | Schema) or String.
37 | """
38 | if value is None:
39 | self.__name = value
40 | elif not isinstance(value, str):
41 | raise TypeError("Name must be set to a String")
42 | else:
43 | self.__name = value
44 |
45 | def get_lane_list(self):
46 | """
47 | Getter for 'lane_list' field.
48 | :return: value of 'lane_list' field.
49 | """
50 | return self.__lane_list
51 |
52 | def set_lane_list(self, value):
53 | """
54 | Setter for 'lane_list' field.
55 | :param value - a new value of 'lane_list' field. Must be a list of Lane objects
56 | """
57 | if value is None or not isinstance(value, list):
58 | raise TypeError("LaneList new value must be a list")
59 | else:
60 | for element in value:
61 | if not isinstance(element, lane.Lane):
62 | raise TypeError("LaneList elements in variable must be of Lane class")
63 | self.__lane_list = value
64 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/lane_type.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Class used for representing tLane of BPMN 2.0 graph
4 | """
5 | import graph.classes.base_element_type as base_element
6 | import graph.classes.lane_set_type as lane_set
7 |
8 |
9 | class Lane(base_element.BaseElement):
10 | """
11 | Class used for representing tLane of BPMN 2.0 graph.
12 | Fields (except inherited):
13 | - name: name of element. Must be either None (name is optional according to BPMN 2.0 XML Schema) or String.
14 | - flow_node_ref_list: a list of String objects (ID of referenced nodes).
15 | - child_lane_set: an object of LaneSet type.
16 | """
17 |
18 | def __init__(self):
19 | """
20 | Default constructor, initializes object fields with new instances.
21 | """
22 | super(Lane, self).__init__()
23 | self.__name = None
24 | self.__flow_node_ref_list = []
25 | self.__child_lane_set = None
26 |
27 | def get_name(self):
28 | """
29 | Getter for 'name' field.
30 | :return: value of 'name' field.
31 | """
32 | return self.__name
33 |
34 | def set_name(self, value):
35 | """
36 | Setter for 'name' field.
37 | :param value - a new value of 'name' field. Must be either None (name is optional according to BPMN 2.0 XML
38 | Schema) or String.
39 | """
40 | if value is None:
41 | self.__name = value
42 | elif not isinstance(value, str):
43 | raise TypeError("Name must be set to a String")
44 | else:
45 | self.__name = value
46 |
47 | def get_flow_node_ref_list(self):
48 | """
49 | Getter for 'flow_node_ref' field.
50 | :return:a value of 'flow_node_ref' field.
51 | """
52 | return self.__flow_node_ref_list
53 |
54 | def set_flow_node_ref_list(self, value):
55 | """
56 | Setter for 'flow_node_ref' field.
57 | :param value - a new value of 'flow_node_ref' field. Must be a list of String objects (ID of referenced nodes).
58 | """
59 | if value is None or not isinstance(value, list):
60 | raise TypeError("FlowNodeRefList new value must be a list")
61 | else:
62 | for element in value:
63 | if not isinstance(element, str):
64 | raise TypeError("FlowNodeRefList elements in variable must be of String class")
65 | self.__flow_node_ref_list = value
66 |
67 | def get_child_lane_set(self):
68 | """
69 | Getter for 'child_lane_set' field.
70 | :return:a value of 'child_lane_set' field.
71 | """
72 | return self.__child_lane_set
73 |
74 | def set_child_lane_set(self, value):
75 | """
76 | Setter for 'child_lane_set' field.
77 | :param value - a new value of 'child_lane_set' field. Must be an object of LaneSet type.
78 | """
79 | if value is None:
80 | self.__child_lane_set = value
81 | elif not isinstance(value, lane_set.LaneSet):
82 | raise TypeError("ChildLaneSet must be a LaneSet")
83 | else:
84 | self.__child_lane_set = value
85 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/message_flow_type.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Class used for representing tMessageFlow of BPMN 2.0 graph
4 | """
5 | import graph.classes.base_element_type as base_element
6 |
7 |
8 | class MessageFlow(base_element.BaseElement):
9 | """
10 | Class used for representing tMessageFlow of BPMN 2.0 graph
11 | """
12 |
13 | def __init__(self, source_ref, target_ref):
14 | """
15 | Default constructor, initializes object fields with new instances.
16 | Fields (except inherited):
17 | - name: name of element. Must be either None (name is optional according to BPMN 2.0 XML Schema) or String.
18 | - source_ref: an ID of source node. Required field. Must be a String type.
19 | - target_ref: an ID of target node. Required field. Must be a String type.
20 | - message_ref: an ID of referenced message element. Must be either None (message_ref is optional according to
21 | BPMN 2.0 XML Schema) or String.
22 | """
23 | if source_ref is None or not isinstance(source_ref, str):
24 | raise TypeError("SourceRef is required and must be set to a String")
25 | if target_ref is None or not isinstance(source_ref, str):
26 | raise TypeError("TargetRef is required and must be set to a String")
27 |
28 | super(MessageFlow, self).__init__()
29 | self.__name = None
30 | self.__source_ref = source_ref
31 | self.__target_ref = target_ref
32 | self.__message_ref = None
33 |
34 | def get_name(self):
35 | """
36 | Getter for 'name' field.
37 | :return:a value of 'name' field.
38 | """
39 | return self.__name
40 |
41 | def set_name(self, value):
42 | """
43 | Setter for 'name' field.
44 | :param value - a new value of 'name' field. Must be either None (name is optional according to BPMN 2.0 XML
45 | Schema) or String.
46 | """
47 | if value is None:
48 | self.__name = value
49 | elif not isinstance(value, str):
50 | raise TypeError("Name must be set to a String")
51 | else:
52 | self.__name = value
53 |
54 | def get_source_ref(self):
55 | """
56 | Getter for 'source_ref' field.
57 | :return: value of 'source_ref' field.
58 | """
59 | return self.__source_ref
60 |
61 | def set_source_ref(self, value):
62 | """
63 | Setter for 'source_ref' field.
64 | :param value - a new value of 'source_ref' field. Must be a String type.
65 | """
66 | if value is None or not isinstance(value, str):
67 | raise TypeError("SourceRef is required and must be set to a String")
68 | else:
69 | self.__source_ref = value
70 |
71 | def get_target_ref(self):
72 | """
73 | Getter for 'target_ref' field.
74 | :return: value of 'target_ref' field.
75 | """
76 | return self.__target_ref
77 |
78 | def set_target_ref(self, value):
79 | """
80 | Setter for 'target_ref' field.
81 | :param value - a new value of 'target_ref' field. Must be a String type.
82 | """
83 | if value is None or not isinstance(value, str):
84 | raise TypeError("TargetRef is required and must be set to a String")
85 | else:
86 | self.__target_ref = value
87 |
88 | def get_message_ref(self):
89 | """
90 | Getter for 'message_ref' field.
91 | :return: value of 'message_ref' field.
92 | """
93 | return self.__message_ref
94 |
95 | def set_message_ref(self, value):
96 | """
97 | Setter for 'message_ref' field.
98 | :param value - a new value of 'message_ref' field. Must be either None (message_ref is optional according to
99 | BPMN 2.0 XML Schema) or String.
100 | """
101 | if value is None:
102 | self.__message_ref = value
103 | if not isinstance(value, str):
104 | raise TypeError("MessageRef must be set to a String")
105 | else:
106 | self.__message_ref = value
107 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/participant_type.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Class used for representing tParticipant of BPMN 2.0 graph
4 | """
5 | import graph.classes.base_element_type as base_element
6 |
7 |
8 | class Participant(base_element.BaseElement):
9 | """
10 | Class used for representing tParticipant of BPMN 2.0 graph
11 | Fields (except inherited):
12 | - name: name of element. Must be either None (name is optional according to BPMN 2.0 XML Schema) or String.
13 | - process_ref: an ID of referenced message element. Must be either None (process_ref is optional according to
14 | BPMN 2.0 XML Schema) or String.
15 | """
16 |
17 | def __init__(self):
18 | """
19 | Default constructor, initializes object fields with new instances.
20 | """
21 | super(Participant, self).__init__()
22 | self.__name = None
23 | self.__process_ref = None
24 |
25 | def get_name(self):
26 | """
27 | Getter for 'name' field.
28 | :return:a value of 'name' field.
29 | """
30 | return self.__name
31 |
32 | def set_name(self, value):
33 | """
34 | Setter for 'name' field.
35 | :param value - a new value of 'name' field. Must be either None (name is optional according to BPMN 2.0 XML
36 | Schema) or String.
37 | """
38 | if value is None:
39 | self.__name = value
40 | elif not isinstance(value, str):
41 | raise TypeError("Name must be set to a String")
42 | else:
43 | self.__name = value
44 |
45 | def get_process_ref(self):
46 | """
47 | Getter for 'process_ref' field.
48 | :return:a value of 'process_ref' field.
49 | """
50 | return self.__process_ref
51 |
52 | def set_process_ref(self, value):
53 | """
54 | Setter for 'process_ref' field.
55 | :param value - a new value of 'process_ref' field. Must be either None (process_ref is optional according to
56 | BPMN 2.0 XML Schema) or String.
57 | """
58 | if not isinstance(value, str):
59 | raise TypeError("ProcessRef must be set to a String")
60 | self.__process_ref = value
61 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/root_element/__init__.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Package init file
4 | """
5 | __all__ = ["callable_element_type", "event_definition_type", "process_type", "root_element_type"]
6 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/root_element/callable_element_type.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Class used for representing tCallableElement of BPMN 2.0 graph
4 | """
5 | import graph.classes.root_element.root_element_type as root_element
6 |
7 |
8 | class CallableElement(root_element.RootElement):
9 | """
10 | Class used for representing tCallableElement of BPMN 2.0 graph.
11 | """
12 |
13 | def __init__(self):
14 | """
15 | Default constructor, initializes object fields with new instances.
16 | """
17 | super(CallableElement, self).__init__()
18 | self.__name = None
19 |
20 | def get_name(self):
21 | """
22 | Getter for 'name' field.
23 | :return:a value of 'name' field.
24 | """
25 | return self.__name
26 |
27 | def set_name(self, value):
28 | """
29 | Setter for 'name' field.
30 | :param value - a new value of 'name' field. Must be either None (name is optional according to BPMN 2.0 XML
31 | Schema) or String.
32 | """
33 | if value is None:
34 | self.__name = value
35 | elif not isinstance(value, str):
36 | raise TypeError("Name must be set to a String")
37 | else:
38 | self.__name = value
39 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/root_element/event_definition_type.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Class used for representing tEventDefinition of BPMN 2.0 graph
4 | """
5 | import graph.classes.root_element.root_element_type as root_element
6 |
7 |
8 | class EventDefinition(root_element.RootElement):
9 | """
10 | Class used for representing tEventDefinition of BPMN 2.0 graph.
11 | """
12 |
13 | def __init__(self):
14 | """
15 | Default constructor, initializes object fields with new instances.
16 | """
17 | super(EventDefinition, self).__init__()
18 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/root_element/process_type.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Class used for representing tProcess of BPMN 2.0 graph
4 | """
5 | import graph.classes.flow_element_type as flow_element
6 | import graph.classes.lane_set_type as lane_set
7 | import graph.classes.root_element.callable_element_type as callable_element
8 |
9 |
10 | class Process(callable_element.CallableElement):
11 | """
12 | Class used for representing tProcess of BPMN 2.0 graph.
13 | """
14 | __process_type_list = ["None", "Public", "Private"]
15 |
16 | def __init__(self):
17 | """
18 | Default constructor, initializes object fields with new instances.
19 | """
20 | super(Process, self).__init__()
21 | self.__process_type = "None"
22 | self.__is_closed = False
23 | self.__is_executable = False
24 | self.__lane_set_list = []
25 | self.__flow_element_list = []
26 |
27 | def get_process_type(self):
28 | """
29 | Getter for 'process_type' field.
30 | :return:a value of 'process_type' field.
31 | """
32 | return self.__process_type
33 |
34 | def set_process_type(self, value):
35 | """
36 | Setter for 'process_type' field.
37 | :param value - a new value of 'process_type' field.
38 | """
39 | if value is None or not isinstance(value, str):
40 | raise TypeError("ProcessType must be set to a String")
41 | elif value not in Process.__process_type_list:
42 | raise ValueError("ProcessType must be one of specified values: 'None', 'Public', 'Private'")
43 | else:
44 | self.__process_type = value
45 |
46 | def is_closed(self):
47 | """
48 | Getter for 'is_closed' field.
49 | :return: value of 'is_closed' field.
50 | """
51 | return self.__is_closed
52 |
53 | def set_is_closed(self, value):
54 | """
55 | Setter for 'is_closed' field.
56 | :param value - a new value of 'is_closed' field. Must be a boolean type. Does not accept None value.
57 | """
58 | if value is None or not isinstance(value, bool):
59 | raise TypeError("IsClosed must be set to a bool")
60 | else:
61 | self.__is_closed = value
62 |
63 | def is_executable(self):
64 | """
65 | Getter for 'is_executable' field.
66 | :return: value of 'is_executable' field.
67 | """
68 | return self.__is_executable
69 |
70 | def set_is_executable(self, value):
71 | """
72 | Setter for 'is_executable' field.
73 | :param value - a new value of 'is_executable' field. Must be a boolean type. Does not accept None value.
74 | """
75 | if value is None or not isinstance(value, bool):
76 | raise TypeError("IsExecutable must be set to a bool")
77 | else:
78 | self.__is_executable = value
79 |
80 | def get_lane_set_list(self):
81 | """
82 | Getter for 'lane_set_list' field.
83 | :return:a value of 'lane_set_list' field.
84 | """
85 | return self.__lane_set_list
86 |
87 | def set_lane_set_list(self, value):
88 | """
89 | Setter for 'lane_set_list' field.
90 | :param value - a new value of 'lane_set_list' field. Must be a list
91 | """
92 | if value is None or not isinstance(value, list):
93 | raise TypeError("LaneSetList new value must be a list")
94 | else:
95 | for element in value:
96 | if not isinstance(element, lane_set.LaneSet):
97 | raise TypeError("LaneSetList elements in variable must be of LaneSet class")
98 | self.__lane_set_list = value
99 |
100 | def get_flow_element_list(self):
101 | """
102 | Getter for 'flow_element_list' field.
103 | :return:a value of 'flow_element_list' field.
104 | """
105 | return self.__flow_element_list
106 |
107 | def set_flow_element_list(self, value):
108 | """
109 | Setter for 'flow_element_list' field.
110 | :param value - a new value of 'flow_element_list' field. Must be a list
111 | """
112 | if value is None or not isinstance(value, list):
113 | raise TypeError("FlowElementList new value must be a list")
114 | else:
115 | for element in value:
116 | if not isinstance(element, flow_element.FlowElement):
117 | raise TypeError("FlowElementList elements in variable must be of FlowElement class")
118 | self.__flow_element_list = value
119 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/root_element/root_element_type.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Class used for representing tRootElement of BPMN 2.0 graph
4 | """
5 | import graph.classes.base_element_type as base_element
6 |
7 |
8 | class RootElement(base_element.BaseElement):
9 | """
10 | Class used for representing tRootElement of BPMN 2.0 graph.
11 | """
12 |
13 | def __init__(self):
14 | """
15 | Default constructor, initializes object fields with new instances.
16 | """
17 | super(RootElement, self).__init__()
18 |
--------------------------------------------------------------------------------
/bpmn_python/graph/classes/sequence_flow_type.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Class used for representing tSequenceFlow of BPMN 2.0 graph
4 | """
5 | import graph.classes.condition_expression_type as condition_expression
6 | import graph.classes.flow_element_type as flow_element
7 |
8 |
9 | class SequenceFlow(flow_element.FlowElement):
10 | """
11 | Class used for representing tSequenceFlow of BPMN 2.0 graph.
12 | Fields (except inherited):
13 | - source_ref: an ID of source node. Required field. Must be a String type.
14 | - target_ref: an ID of target node. Required field. Must be a String type.
15 | - condition_expression: an expression used as condition of flow (conditional flow). Must be either None
16 | (condition_expression is optional according to BPMN 2.0 XML Schema) or String.
17 | - is_immediate: a boolean value.
18 | """
19 |
20 | def __init__(self, source_ref, target_ref):
21 | """
22 | Default constructor, initializes object fields with new instances.
23 | """
24 | if source_ref is None or not isinstance(source_ref, str):
25 | raise TypeError("SourceRef is required and must be set to a String")
26 | if target_ref is None or not isinstance(source_ref, str):
27 | raise TypeError("TargetRef is required and must be set to a String")
28 |
29 | super(SequenceFlow, self).__init__()
30 | self.__source_ref = source_ref
31 | self.__target_ref = target_ref
32 | self.__condition_expression = None
33 | self.__is_immediate = None
34 |
35 | def get_source_ref(self):
36 | """
37 | Getter for 'source_ref' field.
38 | :return: value of 'source_ref' field.
39 | """
40 | return self.__source_ref
41 |
42 | def set_source_ref(self, value):
43 | """
44 | Setter for 'source_ref' field.
45 | :param value - a new value of 'source_ref' field. Required field. Must be a String type.
46 | """
47 | if value is None or not isinstance(value, str):
48 | raise TypeError("SourceRef is required and must be set to a String")
49 | else:
50 | self.__source_ref = value
51 |
52 | def get_target_ref(self):
53 | """
54 | Getter for 'target_ref' field.
55 | :return: value of 'target_ref' field.
56 | """
57 | return self.__target_ref
58 |
59 | def set_target_ref(self, value):
60 | """
61 | Setter for 'target_ref' field.
62 | :param value - a new value of 'target_ref' field. Required field. Must be a String type.
63 | """
64 | if value is None or not isinstance(value, str):
65 | raise TypeError("TargetRef is required and must be set to a String")
66 | else:
67 | self.__target_ref = value
68 |
69 | def is_immediate(self):
70 | """
71 | Getter for 'is_immediate' field.
72 | :return: value of 'is_immediate' field.
73 | """
74 | return self.__is_immediate
75 |
76 | def set_is_immediate(self, value):
77 | """
78 | Setter for 'is_immediate' field.
79 | :param value - a new value of 'is_immediate' field. Must be a boolean type.
80 | """
81 | if value is None:
82 | self.__is_immediate = value
83 | elif not isinstance(value, bool):
84 | raise TypeError("IsImediate must be set to a bool")
85 | else:
86 | self.__is_immediate = value
87 |
88 | def get_condition_expression(self):
89 | """
90 | Getter for 'condition_expression' field.
91 | :return: value of 'condition_expression' field. Must be either None (condition_expression is optional according
92 | to BPMN 2.0 XML Schema) or String.
93 | """
94 | return self.__condition_expression
95 |
96 | def set_condition_expression(self, value):
97 | """
98 | Setter for 'condition_expression' field.
99 | :param value - a new value of 'condition_expression' field.
100 | """
101 | if value is None:
102 | self.__condition_expression = value
103 | if not isinstance(value, condition_expression.ConditionExpression):
104 | raise TypeError("ConditionExpression must be set to an instance of class ConditionExpression")
105 | else:
106 | self.__condition_expression = value
107 |
--------------------------------------------------------------------------------
/bpmn_python/grid_cell_class.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | GridCell represents a single cell with node element on two-dimensional grid layout. It is used in diagram layouting
4 | process
5 | """
6 |
7 |
8 | class GridCell(object):
9 | """
10 | Helper class used for Grid cell representation. Contains cell coordinates (row and column) and reference to fow node
11 | """
12 |
13 | def __init__(self, row, col, node_id):
14 | self.row = row
15 | self.col = col
16 | self.node_id = node_id
17 |
18 | def __str__(self):
19 | return repr(self.row + " " + self.col + " " + self.node_id)
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | SOURCEDIR = source
8 | BUILDDIR = build
9 |
10 | # Put it first so that "make" without argument is like "make help".
11 | help:
12 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
13 |
14 | .PHONY: help Makefile
15 |
16 | # Catch-all target: route all unknown targets to Sphinx using the new
17 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
18 | %: Makefile
19 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
--------------------------------------------------------------------------------
/docs/image/camunda-complex-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KrzyHonk/bpmn-python/6e5e28e3d656dbf5bd3d85d78fe8e3f2fb462629/docs/image/camunda-complex-example.png
--------------------------------------------------------------------------------
/docs/image/camunda-simple-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KrzyHonk/bpmn-python/6e5e28e3d656dbf5bd3d85d78fe8e3f2fb462629/docs/image/camunda-simple-example.png
--------------------------------------------------------------------------------
/docs/image/manual-complex.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KrzyHonk/bpmn-python/6e5e28e3d656dbf5bd3d85d78fe8e3f2fb462629/docs/image/manual-complex.png
--------------------------------------------------------------------------------
/docs/image/manual-simple.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KrzyHonk/bpmn-python/6e5e28e3d656dbf5bd3d85d78fe8e3f2fb462629/docs/image/manual-simple.png
--------------------------------------------------------------------------------
/docs/image/signavio-complex-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KrzyHonk/bpmn-python/6e5e28e3d656dbf5bd3d85d78fe8e3f2fb462629/docs/image/signavio-complex-example.png
--------------------------------------------------------------------------------
/docs/image/signavio-simple-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KrzyHonk/bpmn-python/6e5e28e3d656dbf5bd3d85d78fe8e3f2fb462629/docs/image/signavio-simple-example.png
--------------------------------------------------------------------------------
/docs/requirements-docs.txt:
--------------------------------------------------------------------------------
1 | sphinx
2 | sphinx_autodoc_typehints
3 | sphinxcontrib_trio
4 | sphinx_rtd_theme
5 |
--------------------------------------------------------------------------------
/docs/source/api/bpmn_diagram_exception.rst:
--------------------------------------------------------------------------------
1 | BPMN digram exceptions
2 | ======================
3 |
4 | .. automodule:: bpmn_python.bpmn_diagram_exception
5 | .. autoclass:: BpmnPythonError
6 | :members:
7 |
--------------------------------------------------------------------------------
/docs/source/api/bpmn_diagram_export.rst:
--------------------------------------------------------------------------------
1 | BPMN diagram export
2 | ===================
3 |
4 | .. automodule:: bpmn_python.bpmn_diagram_export
5 | .. autoclass:: BpmnDiagramGraphExport
6 | :members:
7 |
--------------------------------------------------------------------------------
/docs/source/api/bpmn_diagram_import.rst:
--------------------------------------------------------------------------------
1 | BPMN diagram import
2 | ===================
3 |
4 | .. automodule:: bpmn_python.bpmn_diagram_import
5 | .. autoclass:: BpmnDiagramGraphImport
6 | :members:
7 |
--------------------------------------------------------------------------------
/docs/source/api/bpmn_diagram_layouter.rst:
--------------------------------------------------------------------------------
1 | BPMN diagram layouter
2 | =====================
3 |
4 | .. automodule:: bpmn_python.bpmn_diagram_layouter
5 | :members:
6 |
7 |
8 |
--------------------------------------------------------------------------------
/docs/source/api/bpmn_diagram_metrics.rst:
--------------------------------------------------------------------------------
1 | BPMN diagram metrics
2 | ====================
3 |
4 | .. automodule:: bpmn_python.bpmn_diagram_metrics
5 | :members:
6 |
--------------------------------------------------------------------------------
/docs/source/api/bpmn_diagram_rep.rst:
--------------------------------------------------------------------------------
1 | BPMN diagram representation
2 | ===========================
3 |
4 | .. automodule:: bpmn_python.bpmn_diagram_rep
5 | .. autoclass:: BpmnDiagramGraph
6 | :members:
7 |
--------------------------------------------------------------------------------
/docs/source/api/bpmn_diagram_visualizer.rst:
--------------------------------------------------------------------------------
1 | BPMN diagram visualization
2 | ==========================
3 |
4 | .. automodule:: bpmn_python.bpmn_diagram_visualizer
5 | :members:
6 |
--------------------------------------------------------------------------------
/docs/source/api/bpmn_process_csv_export.rst:
--------------------------------------------------------------------------------
1 | BPMN process CSV export
2 | ===================
3 |
4 | .. automodule:: bpmn_python.bpmn_process_csv_export
5 | .. autoclass:: BpmnDiagramGraphCsvExport
6 | :members:
7 |
--------------------------------------------------------------------------------
/docs/source/api/bpmn_process_csv_import.rst:
--------------------------------------------------------------------------------
1 | BPMN process CSV import
2 | =======================
3 |
4 | .. automodule:: bpmn_python.bpmn_process_csv_import
5 | .. autoclass:: BpmnDiagramGraphCSVImport
6 | :members:
7 |
--------------------------------------------------------------------------------
/docs/source/api/grid_cell_class.rst:
--------------------------------------------------------------------------------
1 | GridCell representation
2 | =======================
3 |
4 | .. automodule:: bpmn_python.grid_cell_class
5 | .. autoclass:: GridCell
6 | :members:
7 |
--------------------------------------------------------------------------------
/docs/source/api_reference.rst:
--------------------------------------------------------------------------------
1 | Api reference
2 | =============
3 |
4 | .. toctree::
5 | :maxdepth: 2
6 | :caption: Contents:
7 |
8 | api/bpmn_diagram_import
9 | api/bpmn_diagram_export
10 |
11 | api/bpmn_process_csv_import
12 | api/bpmn_process_csv_export
13 |
14 | api/bpmn_diagram_exception
15 | api/bpmn_diagram_layouter
16 | api/bpmn_diagram_metrics
17 | api/bpmn_diagram_rep
18 | api/bpmn_diagram_visualizer
19 |
20 | api/grid_cell_class
21 |
--------------------------------------------------------------------------------
/docs/source/conf.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Configuration file for the Sphinx documentation builder.
4 | #
5 | # This file does only contain a selection of the most common options. For a
6 | # full list see the documentation:
7 | # http://www.sphinx-doc.org/en/master/config
8 |
9 | # -- Path setup --------------------------------------------------------------
10 |
11 | # If extensions (or modules to document with autodoc) are in another directory,
12 | # add these directories to sys.path here. If the directory is relative to the
13 | # documentation root, use os.path.abspath to make it absolute, like shown here.
14 | #
15 | import os
16 | import sys
17 | sys.path.insert(0, os.path.abspath('../bpmn_python'))
18 |
19 |
20 | # -- Project information -----------------------------------------------------
21 |
22 | project = 'BPMN Python'
23 | copyright = '2019, Izbela Smietana, Krzysztof Honkisz'
24 | author = 'Izbela Smietana, Krzysztof Honkisz'
25 |
26 | # The short X.Y version
27 | version = ''
28 | # The full version, including alpha/beta/rc tags
29 | release = '0.0.19'
30 |
31 |
32 | # -- General configuration ---------------------------------------------------
33 |
34 | # If your documentation needs a minimal Sphinx version, state it here.
35 | #
36 | # needs_sphinx = '1.0'
37 |
38 | # Add any Sphinx extension module names here, as strings. They can be
39 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
40 | # ones.
41 | extensions = [
42 | 'sphinx.ext.autodoc',
43 | 'sphinx.ext.intersphinx',
44 | 'sphinx.ext.imgmath',
45 | 'sphinx.ext.viewcode',
46 | 'sphinx.ext.githubpages',
47 | ]
48 |
49 | # Add any paths that contain templates here, relative to this directory.
50 | templates_path = ['_templates']
51 |
52 | # The suffix(es) of source filenames.
53 | # You can specify multiple suffix as a list of string:
54 | #
55 | # source_suffix = ['.rst', '.md']
56 | source_suffix = '.rst'
57 |
58 | # The master toctree document.
59 | master_doc = 'index'
60 |
61 | # The language for content autogenerated by Sphinx. Refer to documentation
62 | # for a list of supported languages.
63 | #
64 | # This is also used if you do content translation via gettext catalogs.
65 | # Usually you set "language" from the command line for these cases.
66 | language = None
67 |
68 | # List of patterns, relative to source directory, that match files and
69 | # directories to ignore when looking for source files.
70 | # This pattern also affects html_static_path and html_extra_path.
71 | exclude_patterns = []
72 |
73 | # The name of the Pygments (syntax highlighting) style to use.
74 | pygments_style = None
75 |
76 |
77 | # -- Options for HTML output -------------------------------------------------
78 |
79 | # The theme to use for HTML and HTML Help pages. See the documentation for
80 | # a list of builtin themes.
81 | #
82 | html_theme = 'sphinx_rtd_theme'
83 |
84 | # Theme options are theme-specific and customize the look and feel of a theme
85 | # further. For a list of options available for each theme, see the
86 | # documentation.
87 | #
88 | # html_theme_options = {}
89 |
90 | # Add any paths that contain custom static files (such as style sheets) here,
91 | # relative to this directory. They are copied after the builtin static files,
92 | # so a file named "default.css" will overwrite the builtin "default.css".
93 | html_static_path = ['_static']
94 |
95 | # Custom sidebar templates, must be a dictionary that maps document names
96 | # to template names.
97 | #
98 | # The default sidebars (for documents that don't match any pattern) are
99 | # defined by theme itself. Builtin themes are using these templates by
100 | # default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
101 | # 'searchbox.html']``.
102 | #
103 | # html_sidebars = {}
104 |
105 |
106 | # -- Options for HTMLHelp output ---------------------------------------------
107 |
108 | # Output file base name for HTML help builder.
109 | htmlhelp_basename = 'BPMNPythondoc'
110 |
111 |
112 | # -- Options for LaTeX output ------------------------------------------------
113 |
114 | latex_elements = {
115 | # The paper size ('letterpaper' or 'a4paper').
116 | #
117 | # 'papersize': 'letterpaper',
118 |
119 | # The font size ('10pt', '11pt' or '12pt').
120 | #
121 | # 'pointsize': '10pt',
122 |
123 | # Additional stuff for the LaTeX preamble.
124 | #
125 | # 'preamble': '',
126 |
127 | # Latex figure (float) alignment
128 | #
129 | # 'figure_align': 'htbp',
130 | }
131 |
132 | # Grouping the document tree into LaTeX files. List of tuples
133 | # (source start file, target name, title,
134 | # author, documentclass [howto, manual, or own class]).
135 | latex_documents = [
136 | (master_doc, 'BPMNPython.tex', 'BPMN Python Documentation',
137 | 'Izbela Smietana, Krzysztof Honkisz', 'manual'),
138 | ]
139 |
140 |
141 | # -- Options for manual page output ------------------------------------------
142 |
143 | # One entry per manual page. List of tuples
144 | # (source start file, name, description, authors, manual section).
145 | man_pages = [
146 | (master_doc, 'bpmnpython', 'BPMN Python Documentation',
147 | [author], 1)
148 | ]
149 |
150 |
151 | # -- Options for Texinfo output ----------------------------------------------
152 |
153 | # Grouping the document tree into Texinfo files. List of tuples
154 | # (source start file, target name, title, author,
155 | # dir menu entry, description, category)
156 | texinfo_documents = [
157 | (master_doc, 'BPMNPython', 'BPMN Python Documentation',
158 | author, 'BPMNPython', 'One line description of project.',
159 | 'Miscellaneous'),
160 | ]
161 |
162 |
163 | # -- Options for Epub output -------------------------------------------------
164 |
165 | # Bibliographic Dublin Core info.
166 | epub_title = project
167 |
168 | # The unique identifier of the text. This can be a ISBN number
169 | # or the project homepage.
170 | #
171 | # epub_identifier = ''
172 |
173 | # A unique identification for the text.
174 | #
175 | # epub_uid = ''
176 |
177 | # A list of files that should not be packed into the epub file.
178 | epub_exclude_files = ['search.html']
179 |
180 |
181 | # -- Extension configuration -------------------------------------------------
182 |
183 | # -- Options for intersphinx extension ---------------------------------------
184 |
185 | # Example configuration for intersphinx: refer to the Python standard library.
186 | intersphinx_mapping = {'https://docs.python.org/': None}
187 |
188 | # -- Extension configuration -------------------------------------------------
189 | extensions = [
190 | 'sphinx.ext.autodoc',
191 | 'sphinx_autodoc_typehints',
192 | 'sphinxcontrib_trio'
193 | ]
194 | autoclass_content = 'both'
195 |
--------------------------------------------------------------------------------
/docs/source/index.rst:
--------------------------------------------------------------------------------
1 | .. BPMN Python documentation master file, created by
2 | sphinx-quickstart on Sat Jan 5 14:14:58 2019.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | Welcome to BPMN Python's documentation!
7 | =======================================
8 |
9 | .. toctree::
10 | :maxdepth: 2
11 | :caption: Contents:
12 |
13 | self
14 | api_reference
15 |
16 |
17 |
18 | Indices and tables
19 | ==================
20 |
21 | * :ref:`genindex`
22 | * :ref:`modindex`
23 | * :ref:`search`
24 |
25 |
26 |
27 | Overview
28 | --------
29 |
30 | `bpmn_python` is Python client library for importing/exporting BPMN diagrams from an XML file with basic visualization capabilities using `matplotlib `_
31 |
32 |
33 | Key features
34 | ------------
35 |
36 | - parse bpmn representation from xml files
37 | - tinker around and visualize bpmn models
38 |
39 | Installation
40 | ------------
41 |
42 | .. code-block:: bash
43 |
44 | pip install bpmn_python
45 |
46 |
47 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | six
2 | pandas
3 | networkx
4 | matplotlib
5 | pydotplus
6 | pydot
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [metadata]
2 | description-file = README.md
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | import os
3 |
4 | from setuptools import setup, find_packages
5 |
6 |
7 | def read(fname):
8 | """
9 | Utility function to read the README file. Used for the long_description.
10 | :param fname:
11 | :return:
12 | """
13 | return open(os.path.join(os.path.dirname(__file__), fname)).read()
14 |
15 |
16 | setup(
17 | name="bpmn_python",
18 | version="0.0.19-SNAPSHOT",
19 | author="Izbela Smietana, Krzysztof Honkisz",
20 | # author_email = "honkiszkrzystof@gmail.com",
21 | description=("Python library that allows to import/export BPMN diagram (as an XML file) and provides a simple "
22 | "visualization capabilities."),
23 | license="GNU GENERAL PUBLIC LICENSE",
24 | keywords=["bpmn", "xml"],
25 | url="https://github.com/KrzyHonk/bpmn-python",
26 | download_url="https://github.com/KrzyHonk/bpmn-python/tarball/0.0.19-SNAPSHOT",
27 | python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*',
28 | packages=find_packages(exclude=['docs', 'tests*']),
29 | install_requires=read('requirements.txt').split('\n'),
30 | long_description="%s\n%s" % (
31 | read('README.md'),
32 | read('CHANGELOG.md')
33 | ),
34 | long_description_content_type='text/markdown',
35 | classifiers=[
36 | "License :: OSI Approved :: GNU General Public License (GPL)",
37 | "Programming Language :: Python",
38 | "Programming Language :: Python :: 2",
39 | "Programming Language :: Python :: 3",
40 | "Topic :: Scientific/Engineering :: Information Analysis",
41 | "Topic :: Text Processing :: Markup :: XML"
42 | ]
43 | )
44 |
--------------------------------------------------------------------------------
/tests/conftest.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | import pytest
4 |
5 |
6 | @pytest.fixture(scope="module", autouse=True)
7 | def change_path_to_script_location(request):
8 | test_dir = os.path.dirname(request.module.__file__)
9 | os.chdir(test_dir)
10 |
11 |
12 | @pytest.fixture(scope="session", autouse=True)
13 | def reset_path(request):
14 | initial_dir = os.getcwd()
15 | yield
16 | os.chdir(initial_dir)
17 |
--------------------------------------------------------------------------------
/tests/csv_export/csv_export_test.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Unit tests for exporting process to CSV functionality.
4 | """
5 |
6 | import os
7 | import unittest
8 |
9 | import bpmn_python.bpmn_diagram_rep as diagram
10 |
11 |
12 | class CsvExportTests(unittest.TestCase):
13 | """
14 | This class contains test for manual diagram generation functionality.
15 | """
16 | output_directory = "./output/test-csv-export/"
17 | example_directory = "../examples/csv_export/"
18 |
19 | def test_csv_export_bank_account_example(self):
20 | # TODO not working correctly, problem with nested splits
21 | bpmn_graph = diagram.BpmnDiagramGraph()
22 | bpmn_graph.load_diagram_from_xml_file(os.path.abspath(self.example_directory + "bank-account-process.bpmn"))
23 | bpmn_graph.export_csv_file(self.output_directory, "bank-account-process.csv")
24 |
25 | def test_csv_export_checkin_process_example(self):
26 | bpmn_graph = diagram.BpmnDiagramGraph()
27 | bpmn_graph.load_diagram_from_xml_file(os.path.abspath(self.example_directory + "checkin-process.bpmn"))
28 | bpmn_graph.export_csv_file(self.output_directory, "checkin-process.csv")
29 |
30 | def test_csv_export_credit_process_example(self):
31 | bpmn_graph = diagram.BpmnDiagramGraph()
32 | bpmn_graph.load_diagram_from_xml_file(os.path.abspath(self.example_directory + "credit-process.bpmn"))
33 | bpmn_graph.export_csv_file(self.output_directory, "credit-process.csv")
34 |
35 | def test_csv_export_order_processing_example(self):
36 | # TODO not working correctly, problem with nested splits
37 | bpmn_graph = diagram.BpmnDiagramGraph()
38 | bpmn_graph.load_diagram_from_xml_file(os.path.abspath(self.example_directory + "order-processing.bpmn"))
39 | bpmn_graph.export_csv_file(self.output_directory, "order-processing.csv")
40 |
41 | def test_csv_export_pizza_order_example(self):
42 | bpmn_graph = diagram.BpmnDiagramGraph()
43 | bpmn_graph.load_diagram_from_xml_file(os.path.abspath(self.example_directory + "pizza-order.bpmn"))
44 | bpmn_graph.export_csv_file(self.output_directory, "pizza-order.csv")
45 |
46 | def test_csv_export_tram_process_example(self):
47 | bpmn_graph = diagram.BpmnDiagramGraph()
48 | bpmn_graph.load_diagram_from_xml_file(os.path.abspath(self.example_directory + "tram-process.bpmn"))
49 | # TODO Problem with the loops
50 | #bpmn_graph.export_csv_file(self.output_directory, "tram-process.csv")
51 |
52 | def test_csv_export_manual_simple_diagram(self):
53 | bpmn_graph = diagram.BpmnDiagramGraph()
54 | bpmn_graph.create_new_diagram_graph(diagram_name="diagram1")
55 | process_id = bpmn_graph.add_process_to_diagram()
56 | [start_id, _] = bpmn_graph.add_start_event_to_diagram(process_id, start_event_name="Start event",
57 | start_event_definition="timer")
58 | [task1_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="Task 1")
59 | [subprocess1_id, _] = bpmn_graph.add_subprocess_to_diagram(process_id, subprocess_name="Subprocess 1")
60 | [subprocess2_id, _] = bpmn_graph.add_subprocess_to_diagram(process_id, subprocess_name="Subprocess 2")
61 | [task2_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="Task 2")
62 | [end_id, _] = bpmn_graph.add_end_event_to_diagram(process_id, end_event_name="End event",
63 | end_event_definition="message")
64 |
65 | bpmn_graph.add_sequence_flow_to_diagram(process_id, start_id, task1_id,
66 | sequence_flow_name="start_to_task_one")
67 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task1_id, subprocess1_id,
68 | sequence_flow_name="task_one_to_subprocess_one")
69 | bpmn_graph.add_sequence_flow_to_diagram(process_id, subprocess1_id, subprocess2_id,
70 | sequence_flow_name="subprocess_one_to_subprocess_two")
71 | bpmn_graph.add_sequence_flow_to_diagram(process_id, subprocess2_id, task2_id,
72 | sequence_flow_name="subprocess_two_to_task_two")
73 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task2_id, end_id,
74 | sequence_flow_name="task_two_to_end")
75 |
76 | bpmn_graph.export_csv_file(self.output_directory, "simple_diagram.csv")
77 | bpmn_graph.export_xml_file(self.output_directory, "simple_diagram.bpmn")
78 |
79 | def test_csv_export_diagram_with_exclusive_parallel_gateway(self):
80 | bpmn_graph = diagram.BpmnDiagramGraph()
81 | bpmn_graph.create_new_diagram_graph(diagram_name="diagram1")
82 | process_id = bpmn_graph.add_process_to_diagram()
83 | [start_id, _] = bpmn_graph.add_start_event_to_diagram(process_id, start_event_name="Start event",
84 | start_event_definition="timer")
85 | [task1_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="Task 1")
86 |
87 | [exclusive_gate_fork_id, _] = bpmn_graph.add_exclusive_gateway_to_diagram(process_id,
88 | gateway_name="Exclusive gate fork")
89 | [task2_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="Task 2")
90 | [task3_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="Task 3")
91 | [task6_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="Task 6")
92 | [exclusive_gate_join_id, _] = bpmn_graph.add_exclusive_gateway_to_diagram(process_id,
93 | gateway_name="Exclusive gate join")
94 |
95 | [parallel_gate_fork_id, _] = bpmn_graph.add_parallel_gateway_to_diagram(process_id,
96 | gateway_name="Parallel gateway fork")
97 | [task4_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="Task 4")
98 | [task5_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="Task 5")
99 | [parallel_gate_join_id, _] = bpmn_graph.add_parallel_gateway_to_diagram(process_id,
100 | gateway_name="Parallel gateway join")
101 |
102 | [end_id, _] = bpmn_graph.add_end_event_to_diagram(process_id, end_event_name="End event",
103 | end_event_definition="message")
104 |
105 | bpmn_graph.add_sequence_flow_to_diagram(process_id, start_id, task1_id,
106 | sequence_flow_name="Start to one")
107 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task1_id, exclusive_gate_fork_id,
108 | sequence_flow_name="Task one to exclusive fork")
109 | bpmn_graph.add_sequence_flow_to_diagram(process_id, exclusive_gate_fork_id, task2_id,
110 | sequence_flow_name="Exclusive fork to task two")
111 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task2_id, task3_id,
112 | sequence_flow_name="Task two to task three")
113 | bpmn_graph.add_sequence_flow_to_diagram(process_id, exclusive_gate_fork_id, parallel_gate_fork_id,
114 | sequence_flow_name="Exclusive fork to parallel fork")
115 | bpmn_graph.add_sequence_flow_to_diagram(process_id, parallel_gate_fork_id, task4_id,
116 | sequence_flow_name="Parallel fork to task four")
117 | bpmn_graph.add_sequence_flow_to_diagram(process_id, parallel_gate_fork_id, task5_id,
118 | sequence_flow_name="Parallel fork to task five")
119 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task4_id, parallel_gate_join_id,
120 | sequence_flow_name="Task four to parallel join")
121 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task5_id, parallel_gate_join_id,
122 | sequence_flow_name="Task five to parallel join")
123 | bpmn_graph.add_sequence_flow_to_diagram(process_id, parallel_gate_join_id, task6_id,
124 | sequence_flow_name="Parallel join to task six")
125 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task3_id, exclusive_gate_join_id,
126 | sequence_flow_name="Task three to exclusive join")
127 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task6_id, exclusive_gate_join_id,
128 | sequence_flow_name="Task six to exclusive join")
129 | bpmn_graph.add_sequence_flow_to_diagram(process_id, exclusive_gate_join_id, end_id,
130 | sequence_flow_name="Exclusive join to end event")
131 |
132 | bpmn_graph.export_csv_file(self.output_directory, "exclusive_parallel_gateways_diagram.csv")
133 | bpmn_graph.export_xml_file(self.output_directory, "exclusive_parallel_gateways_diagram.bpmn")
134 |
135 | def test_csv_export_diagram_with_inclusive_parallel_gateway(self):
136 | bpmn_graph = diagram.BpmnDiagramGraph()
137 | bpmn_graph.create_new_diagram_graph(diagram_name="diagram1")
138 | process_id = bpmn_graph.add_process_to_diagram()
139 | [start_id, _] = bpmn_graph.add_start_event_to_diagram(process_id, start_event_name="Start event",
140 | start_event_definition="timer")
141 | [task1_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="Task 1")
142 |
143 | [exclusive_gate_fork_id, _] = bpmn_graph.add_inclusive_gateway_to_diagram(process_id,
144 | gateway_name="Inclusive gate fork")
145 | [task2_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="Task 2")
146 | [task3_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="Task 3")
147 | [task6_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="Task 6")
148 | [exclusive_gate_join_id, _] = bpmn_graph.add_inclusive_gateway_to_diagram(process_id,
149 | gateway_name="Inclusive gate join")
150 |
151 | [parallel_gate_fork_id, _] = bpmn_graph.add_parallel_gateway_to_diagram(process_id,
152 | gateway_name="Parallel gateway fork")
153 | [task4_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="Task 4")
154 | [task5_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="Task 5")
155 | [parallel_gate_join_id, _] = bpmn_graph.add_parallel_gateway_to_diagram(process_id,
156 | gateway_name="Parallel gateway join")
157 |
158 | [end_id, _] = bpmn_graph.add_end_event_to_diagram(process_id, end_event_name="End event",
159 | end_event_definition="message")
160 |
161 | bpmn_graph.add_sequence_flow_to_diagram(process_id, start_id, task1_id,
162 | sequence_flow_name="Start to one")
163 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task1_id, exclusive_gate_fork_id,
164 | sequence_flow_name="Task one to exclusive fork")
165 | bpmn_graph.add_sequence_flow_to_diagram(process_id, exclusive_gate_fork_id, task2_id,
166 | sequence_flow_name="Condition: approved")
167 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task2_id, task3_id,
168 | sequence_flow_name="Task two to task three")
169 | bpmn_graph.add_sequence_flow_to_diagram(process_id, exclusive_gate_fork_id, parallel_gate_fork_id,
170 | sequence_flow_name="Condition: rejected")
171 | bpmn_graph.add_sequence_flow_to_diagram(process_id, parallel_gate_fork_id, task4_id,
172 | sequence_flow_name="Parallel fork to task four")
173 | bpmn_graph.add_sequence_flow_to_diagram(process_id, parallel_gate_fork_id, task5_id,
174 | sequence_flow_name="Parallel fork to task five")
175 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task4_id, parallel_gate_join_id,
176 | sequence_flow_name="Task four to parallel join")
177 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task5_id, parallel_gate_join_id,
178 | sequence_flow_name="Task five to parallel join")
179 | bpmn_graph.add_sequence_flow_to_diagram(process_id, parallel_gate_join_id, task6_id,
180 | sequence_flow_name="Parallel join to task six")
181 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task3_id, exclusive_gate_join_id,
182 | sequence_flow_name="Task three to exclusive join")
183 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task6_id, exclusive_gate_join_id,
184 | sequence_flow_name="Task six to exclusive join")
185 | bpmn_graph.add_sequence_flow_to_diagram(process_id, exclusive_gate_join_id, end_id,
186 | sequence_flow_name="Exclusive join to end event")
187 |
188 | bpmn_graph.export_csv_file(self.output_directory, "inclusive_parallel_gateways_diagram.csv")
189 | bpmn_graph.export_xml_file(self.output_directory, "inclusive_parallel_gateways_diagram.bpmn")
190 |
191 |
192 | if __name__ == '__main__':
193 | unittest.main()
194 |
--------------------------------------------------------------------------------
/tests/csv_import/csv_import_test.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Unit tests for exporting process to CSV functionality.
4 | """
5 |
6 | import filecmp
7 | import os
8 | import unittest
9 |
10 | import bpmn_python.bpmn_diagram_rep as diagram
11 |
12 |
13 | class CsvExportTests(unittest.TestCase):
14 | """
15 | This class contains test for manual diagram generation functionality.
16 | """
17 | output_directory = "./output/"
18 | input_directory = "./input/"
19 |
20 | def test_csv_import_csv_export(self):
21 | processes = ["pizza-order", "airline-checkin", "order-processing"]
22 |
23 | for process in processes:
24 | bpmn_graph = diagram.BpmnDiagramGraph()
25 | bpmn_graph.load_diagram_from_csv_file(os.path.abspath(self.input_directory + process + ".csv"))
26 | bpmn_graph.export_csv_file(self.output_directory, process + ".csv")
27 | cmp_result = filecmp.cmp(self.input_directory + process + ".csv", self.output_directory, process + ".csv")
28 | # unittest.TestCase.assertTrue(self, cmp_result) # unfortunatelly csv export has bugs
29 | bpmn_graph.export_xml_file_no_di(self.output_directory, process + ".bpmn")
30 |
31 |
32 | if __name__ == '__main__':
33 | unittest.main()
34 |
--------------------------------------------------------------------------------
/tests/csv_import/input/airline-checkin.csv:
--------------------------------------------------------------------------------
1 | Order,Activity,Condition,Who,Subprocess,Terminated
2 | 0,start,,,,
3 | 1,Validate Passenger Ticket & Identification,,Check In Counter,,
4 | 2a,Confirm Itinerary,Validity,,yes,
5 | 2b,Reject Passenger,else,,,yes
6 | 3,Ask Passenger for Prohibited Objects,,,,
7 | 4a,Remove Prohibited Objects,Prohibited Objects,,,
8 | 4b,goto 5,else,,,
9 | 5,Ask Passenger for Baggages,,,,
10 | 6,Weight Baggages,,,,
11 | 7,Calculate Additional Fees,,,,
12 | 8,Inform Passenger of Additional Fees,,,,
13 | 9,Collect Payment of Fees,,,,
14 | 10a,Generate and Print Boarding Pass,,,,
15 | 10b1,Generate and Print Baggage Tags,,,,
16 | 10b2,Identify and Move Baggages,,,,
17 | 11,"Hand out Boarding Pass, Ticket and Identification",,,,yes
--------------------------------------------------------------------------------
/tests/csv_import/input/order-processing.csv:
--------------------------------------------------------------------------------
1 | Order,Activity,Condition,Who,Subprocess,Terminated
2 | 0,start,,,,
3 | 1,Receive Order,,,,
4 | 2a1,Fill Order,Accepted,,,
5 | 2a2a1,Send Invoice,,,,
6 | 2a2a2,Make Payment,,,,
7 | 2a2a3,Accept Payment,,,,
8 | 2a2b,Ship Order,,,,
9 | 2b,goto 3,Rejected,,,
10 | 3,Close Order,,,,yes
--------------------------------------------------------------------------------
/tests/csv_import/input/pizza-order.csv:
--------------------------------------------------------------------------------
1 | Order,Activity,Condition,Who,Subprocess,Terminated
2 | 0,Receive pizza order,,,,
3 | 1,Answer customer call,,,yes,
4 | 2,Assign the Order,,,,
5 | 3,Prepare the Pizza,,,,
6 | 4,Cook the Pizza,,,,
7 | 5a1,Package the Pizza,,,,
8 | 5b1,Assign the Delivery,,,yes,
9 | 6,Deliver the Pizza,,,,
10 | 7,Receive Payment,,,,
11 | 8,,,,,yes
12 |
--------------------------------------------------------------------------------
/tests/examples/metrics/crossing_point_test.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | sid-19A536E5-4417-4A2E-A752-853AE4AFC249
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | sid-19A536E5-4417-4A2E-A752-853AE4AFC249
20 | sid-825EB574-A785-47A1-BE52-782DD0537EED
21 |
22 |
23 |
24 |
25 |
26 | sid-825EB574-A785-47A1-BE52-782DD0537EED
27 | sid-3465A198-0810-4824-9607-F511302F6A3F
28 | sid-3FD51905-6FF2-44BD-BE0D-2EDDC1FF89CD
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | sid-3465A198-0810-4824-9607-F511302F6A3F
37 | sid-5181181C-4CD8-42B2-AC4C-5CFC05C639BC
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | sid-3FD51905-6FF2-44BD-BE0D-2EDDC1FF89CD
46 | sid-CCF0AC4F-620F-4BF0-85E1-179B722C58C3
47 |
48 |
49 |
50 |
51 |
52 | sid-5181181C-4CD8-42B2-AC4C-5CFC05C639BC
53 | sid-CCF0AC4F-620F-4BF0-85E1-179B722C58C3
54 | sid-13EFCF49-FB3D-45DC-801C-923D7B8EBB7A
55 |
56 |
57 |
58 |
59 |
60 | sid-13EFCF49-FB3D-45DC-801C-923D7B8EBB7A
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
--------------------------------------------------------------------------------
/tests/examples/xml_import_export/bpmn_editor_simple_example.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/tests/examples/xml_import_export/camunda_simple_example.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SequenceFlow_0li1b0t
6 |
7 |
8 | SequenceFlow_0li1b0t
9 | SequenceFlow_0zl3cow
10 |
11 |
12 |
13 | SequenceFlow_0zl3cow
14 | SequenceFlow_0yk155p
15 | SequenceFlow_1foquue
16 |
17 |
18 |
19 | SequenceFlow_0yk155p
20 | SequenceFlow_0g72tcb
21 |
22 |
23 |
24 | SequenceFlow_1foquue
25 | SequenceFlow_0n4g88a
26 |
27 |
28 |
29 | SequenceFlow_0n4g88a
30 | SequenceFlow_0g72tcb
31 | SequenceFlow_15jrpi8
32 |
33 |
34 |
35 |
36 | SequenceFlow_15jrpi8
37 | SequenceFlow_0uvjgsv
38 |
39 |
40 |
41 | SequenceFlow_0uvjgsv
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
--------------------------------------------------------------------------------
/tests/examples/xml_import_export/default-conditional-flow-example.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | sid-F7BF2C58-D4B4-48BD-B614-83A3FB477A15
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | sid-F7BF2C58-D4B4-48BD-B614-83A3FB477A15
20 | sid-8E374108-901D-4CF5-80EA-B6B4C30B3BE0
21 | sid-EF041DBC-8714-4D69-9098-1A4BF0BC56F4
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | sid-EF041DBC-8714-4D69-9098-1A4BF0BC56F4
30 | sid-FA8F2F2D-D5A9-4C92-9B2F-5DFBF1B9B713
31 |
32 |
33 |
34 |
35 |
36 | sid-FA8F2F2D-D5A9-4C92-9B2F-5DFBF1B9B713
37 | sid-9AE25A5F-D224-40C4-9E9A-4548440BABBC
38 | sid-561AE46F-B492-4175-A227-49CF380CAED0
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | sid-8E374108-901D-4CF5-80EA-B6B4C30B3BE0
47 | sid-9AE25A5F-D224-40C4-9E9A-4548440BABBC
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | sid-561AE46F-B492-4175-A227-49CF380CAED0
56 | sid-BE1B3A8A-8FB0-4DEA-8FAE-811767F6287C
57 |
58 |
59 |
60 |
61 |
62 | sid-BE1B3A8A-8FB0-4DEA-8FAE-811767F6287C
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | x == 1
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
--------------------------------------------------------------------------------
/tests/examples/xml_import_export/signavio_simple_example.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | sid-D4C8D513-A3D2-430F-836C-930BE005590E
13 | sid-28CB40A0-E0AD-4C80-8BAD-2499511C4516
14 |
15 |
16 |
17 |
18 |
19 | sid-D4C8D513-A3D2-430F-836C-930BE005590E
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | sid-ED4CB419-74A8-48D8-A512-E590069C69CC
28 | sid-A439209E-6F49-4B01-A3F6-B1EF6CE59433
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | sid-8CA3CBB6-5B23-48F3-9AF4-B2CEBE1E03C9
37 | sid-67F91195-7B66-4198-9C8A-7FF8D2892E9F
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | sid-4604D1B4-A8F1-4664-8953-063F1CB53E8D
46 | sid-1C498761-8113-4E90-9B0B-3EAD775FED3F
47 |
48 |
49 |
50 |
51 |
52 | sid-1C498761-8113-4E90-9B0B-3EAD775FED3F
53 |
54 |
55 |
56 |
57 |
58 | sid-28CB40A0-E0AD-4C80-8BAD-2499511C4516
59 | sid-ED4CB419-74A8-48D8-A512-E590069C69CC
60 | sid-8CA3CBB6-5B23-48F3-9AF4-B2CEBE1E03C9
61 |
62 |
63 |
64 |
65 |
66 | sid-A439209E-6F49-4B01-A3F6-B1EF6CE59433
67 | sid-67F91195-7B66-4198-9C8A-7FF8D2892E9F
68 | sid-4604D1B4-A8F1-4664-8953-063F1CB53E8D
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
--------------------------------------------------------------------------------
/tests/layouter/layouter_test.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Test unit, using simple graph made in BPMNEditor editor for import/export operation
4 | """
5 | import unittest
6 |
7 | import bpmn_python.bpmn_diagram_layouter as layouter
8 | import bpmn_python.bpmn_diagram_rep as diagram
9 |
10 | class BPMNEditorTests(unittest.TestCase):
11 | """
12 | This class contains test for bpmn-python package functionality using an example BPMN diagram created in BPMNEditor.
13 | """
14 | output_directory = "./output/layouter/"
15 |
16 | def test_layouter_manually_created_diagram_simple_case(self):
17 | """
18 | Test for importing a simple BPMNEditor diagram example (as BPMN 2.0 XML) into inner representation
19 | and generating layout for it
20 | """
21 | output_file = "layouter_simple_case.xml"
22 | bpmn_graph = diagram.BpmnDiagramGraph()
23 | bpmn_graph.create_new_diagram_graph(diagram_name="diagram1")
24 | process_id = bpmn_graph.add_process_to_diagram()
25 | [start_id, _] = bpmn_graph.add_start_event_to_diagram(process_id, start_event_name="start_event")
26 | [task1_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="task1")
27 | bpmn_graph.add_sequence_flow_to_diagram(process_id, start_id, task1_id, "start_to_one")
28 |
29 | [task2_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="task2")
30 | [end_id, _] = bpmn_graph.add_end_event_to_diagram(process_id, end_event_name="end_event")
31 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task1_id, task2_id, "one_to_two")
32 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task2_id, end_id, "two_to_end")
33 |
34 | layouter.generate_layout(bpmn_graph)
35 | bpmn_graph.export_xml_file(self.output_directory, output_file)
36 |
37 | def test_layouter_manually_created_diagram_split_join_case(self):
38 | """
39 | Test for importing a simple BPMNEditor diagram example (as BPMN 2.0 XML) into inner representation
40 | and generating layout for it
41 | """
42 | output_file = "layouter_split_join_case.xml"
43 | bpmn_graph = diagram.BpmnDiagramGraph()
44 | bpmn_graph.create_new_diagram_graph(diagram_name="diagram1")
45 | process_id = bpmn_graph.add_process_to_diagram()
46 | [start_id, _] = bpmn_graph.add_start_event_to_diagram(process_id, start_event_name="start_event")
47 | [task1_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="task1")
48 | bpmn_graph.add_sequence_flow_to_diagram(process_id, start_id, task1_id, "start_to_one")
49 |
50 | [exclusive_gate_fork_id, _] = bpmn_graph.add_exclusive_gateway_to_diagram(process_id,
51 | gateway_name="exclusive_gate_fork")
52 | [task1_ex_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="task1_ex")
53 | [task2_ex_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="task2_ex")
54 | [exclusive_gate_join_id, _] = bpmn_graph.add_exclusive_gateway_to_diagram(process_id,
55 | gateway_name="exclusive_gate_join")
56 |
57 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task1_id, exclusive_gate_fork_id, "one_to_ex_fork")
58 | bpmn_graph.add_sequence_flow_to_diagram(process_id, exclusive_gate_fork_id, task1_ex_id, "ex_fork_to_ex_one")
59 | bpmn_graph.add_sequence_flow_to_diagram(process_id, exclusive_gate_fork_id, task2_ex_id, "ex_fork_to_ex_two")
60 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task1_ex_id, exclusive_gate_join_id, "ex_one_to_ex_join")
61 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task2_ex_id, exclusive_gate_join_id, "ex_two_to_ex_join")
62 |
63 | [task2_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="task2")
64 | [end_id, _] = bpmn_graph.add_end_event_to_diagram(process_id, end_event_name="end_event")
65 | bpmn_graph.add_sequence_flow_to_diagram(process_id, exclusive_gate_join_id, task2_id, "ex_join_to_two")
66 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task2_id, end_id, "two_to_end")
67 |
68 | layouter.generate_layout(bpmn_graph)
69 | bpmn_graph.export_xml_file(self.output_directory, output_file)
70 |
71 | def test_layouter_manually_created_diagram_cycle_case(self):
72 | """
73 | Test for importing a simple BPMNEditor diagram example (as BPMN 2.0 XML) into inner representation
74 | and generating layout for it
75 | """
76 | output_file = "layouter_cycle_case.xml"
77 | bpmn_graph = diagram.BpmnDiagramGraph()
78 | bpmn_graph.create_new_diagram_graph(diagram_name="diagram1")
79 | process_id = bpmn_graph.add_process_to_diagram()
80 | [start_id, _] = bpmn_graph.add_start_event_to_diagram(process_id, start_event_name="start_event")
81 | [task1_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="task1")
82 | bpmn_graph.add_sequence_flow_to_diagram(process_id, start_id, task1_id, "start_to_one")
83 |
84 | [exclusive_gate_fork_id, _] = bpmn_graph.add_exclusive_gateway_to_diagram(process_id,
85 | gateway_name="exclusive_gate_fork")
86 | [task1_ex_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="task1_ex")
87 | [task2_ex_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="task2_ex")
88 | [exclusive_gate_join_id, _] = bpmn_graph.add_exclusive_gateway_to_diagram(process_id,
89 | gateway_name="exclusive_gate_join")
90 |
91 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task1_id, exclusive_gate_fork_id, "one_to_ex_fork")
92 | bpmn_graph.add_sequence_flow_to_diagram(process_id, exclusive_gate_fork_id, task1_ex_id, "ex_fork_to_ex_one")
93 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task2_ex_id, exclusive_gate_fork_id, "ex_two_to_ex_fork")
94 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task1_ex_id, exclusive_gate_join_id, "ex_one_to_ex_join")
95 | bpmn_graph.add_sequence_flow_to_diagram(process_id, exclusive_gate_join_id, task2_ex_id, "ex_join_to_ex_two")
96 |
97 | [task2_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="task2")
98 | [end_id, _] = bpmn_graph.add_end_event_to_diagram(process_id, end_event_name="end_event")
99 | bpmn_graph.add_sequence_flow_to_diagram(process_id, exclusive_gate_join_id, task2_id, "ex_join_to_two")
100 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task2_id, end_id, "two_to_end")
101 |
102 | layouter.generate_layout(bpmn_graph)
103 | bpmn_graph.export_xml_file(self.output_directory, output_file)
104 |
105 |
106 | if __name__ == '__main__':
107 | unittest.main()
108 |
--------------------------------------------------------------------------------
/tests/metrics/layout_metrics_test.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Layout metrics computing tests
4 | """
5 | import os
6 | import unittest
7 |
8 | import bpmn_python.diagram_layout_metrics as metrics
9 | import bpmn_python.bpmn_diagram_rep as diagram
10 |
11 |
12 | class MetricsTests(unittest.TestCase):
13 | crossing_points_example_path = "../examples/metrics/crossing_point_test.bpmn"
14 | cycles_example_path = "../examples/metrics/cycles_test.bpmn"
15 |
16 | @staticmethod
17 | def load_example_diagram(filepath):
18 | bpmn_graph = diagram.BpmnDiagramGraph()
19 | bpmn_graph.load_diagram_from_xml_file(os.path.abspath(filepath))
20 | return bpmn_graph
21 |
22 | def test_count_crossing_points(self):
23 | bpmn_graph = MetricsTests.load_example_diagram(self.crossing_points_example_path)
24 | cross_points = metrics.count_crossing_points(bpmn_graph)
25 | self.assertEqual(cross_points, 6, "Crossing points count does not match")
26 |
27 | def test_count_segments(self):
28 | bpmn_graph = MetricsTests.load_example_diagram(self.crossing_points_example_path)
29 | segments_count = metrics.count_segments(bpmn_graph)
30 | self.assertEqual(segments_count, 25, "Segments count does not match")
31 |
32 | def test_compute_longest_path(self):
33 | bpmn_graph = MetricsTests.load_example_diagram(self.cycles_example_path)
34 | (longest_path, longest_path_len) = metrics.compute_longest_path(bpmn_graph)
35 | self.assertEqual(longest_path_len, 9, "Path length does not match")
36 |
37 | def test_compute_longest_path_tasks(self):
38 | bpmn_graph = MetricsTests.load_example_diagram(self.cycles_example_path)
39 | (longest_path, longest_path_len) = metrics.compute_longest_path_tasks(bpmn_graph)
40 | self.assertEqual(longest_path_len, 6, "Path length does not match")
41 |
42 |
43 | if __name__ == '__main__':
44 | unittest.main()
45 |
--------------------------------------------------------------------------------
/tests/metrics/test_bpmn_diagram_metrics.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Unit tests to test the complexity metrics implemented of BPMN models
4 | """
5 | import os
6 | import unittest
7 |
8 | from bpmn_python import bpmn_diagram_metrics as metrics
9 | from bpmn_python.bpmn_diagram_rep import BpmnDiagramGraph
10 |
11 |
12 | class BPMNComplexityMetricsTests(unittest.TestCase):
13 |
14 | def setUp(self):
15 |
16 | self.models = {name: BpmnDiagramGraph()
17 | for name in ['SIMPLE', 'COMPLEX', 'WITH_CYCLES', 'WITH_CROSSING_POINT']
18 | }
19 |
20 | self.models['SIMPLE'].load_diagram_from_xml_file(
21 | os.path.abspath("../examples/xml_import_export/bpmn_editor_simple_example.xml")),
22 | self.models['COMPLEX'].load_diagram_from_xml_file(
23 | os.path.abspath("../examples/xml_import_export/camunda_complex_example.bpmn")),
24 | self.models['WITH_CYCLES'].load_diagram_from_xml_file(
25 | os.path.abspath("../examples/metrics/cycles_test.bpmn")),
26 | self.models['WITH_CROSSING_POINT'].load_diagram_from_xml_file(
27 | os.path.abspath("../examples/metrics/crossing_point_test.bpmn")),
28 |
29 | def testTNSEMetricForSimpleModel(self):
30 |
31 | self.assertEqual(
32 | metrics.TNSE_metric(self.models['SIMPLE']), 1
33 | )
34 |
35 | def testTNSEMetricForComplexModel(self):
36 |
37 | self.assertEqual(
38 | metrics.TNSE_metric(self.models['COMPLEX']), 1
39 | )
40 |
41 | def testTNSEForModelWithCycles(self):
42 |
43 | self.assertEqual(
44 | metrics.TNSE_metric(self.models['WITH_CYCLES']), 1
45 | )
46 |
47 | def testTNSEForModelWithCrossingPoint(self):
48 | self.assertEqual(
49 | metrics.TNSE_metric(self.models['WITH_CROSSING_POINT']), 1
50 | )
51 |
52 | def testTNIEMetricForSimpleModel(self):
53 |
54 | self.assertEqual(
55 | metrics.TNIE_metric(self.models['SIMPLE']), 0
56 | )
57 |
58 | def testTNIEMetricForComplexModel(self):
59 |
60 | self.assertEqual(
61 | metrics.TNIE_metric(self.models['COMPLEX']), 2
62 | )
63 |
64 | def testTNIEForModelWithCycles(self):
65 |
66 | self.assertEqual(
67 | metrics.TNIE_metric(self.models['WITH_CYCLES']), 0
68 | )
69 |
70 | def testTNIEForModelWithCrossingPoint(self):
71 | self.assertEqual(
72 | metrics.TNIE_metric(self.models['WITH_CROSSING_POINT']), 0
73 | )
74 |
75 | def testTNEEMetricForSimpleModel(self):
76 |
77 | self.assertEqual(
78 | metrics.TNEE_metric(self.models['SIMPLE']), 1
79 | )
80 |
81 | def testTNEEMetricForComplexModel(self):
82 |
83 | self.assertEqual(
84 | metrics.TNEE_metric(self.models['COMPLEX']), 1
85 | )
86 |
87 | def testTNEEForModelWithCycles(self):
88 |
89 | self.assertEqual(
90 | metrics.TNEE_metric(self.models['WITH_CYCLES']), 2
91 | )
92 |
93 | def testTNEEForModelWithCrossingPoint(self):
94 | self.assertEqual(
95 | metrics.TNEE_metric(self.models['WITH_CROSSING_POINT']), 1
96 | )
97 |
98 | def testTNEMetricForSimpleModel(self):
99 |
100 | self.assertEqual(
101 | metrics.TNE_metric(self.models['SIMPLE']), 2
102 | )
103 |
104 | def testTNEMetricForComplexModel(self):
105 |
106 | self.assertEqual(
107 | metrics.TNE_metric(self.models['COMPLEX']), 4
108 | )
109 |
110 | def testTNEForModelWithCycles(self):
111 |
112 | self.assertEqual(
113 | metrics.TNE_metric(self.models['WITH_CYCLES']), 3
114 | )
115 |
116 | def testTNEForModelWithCrossingPoint(self):
117 | self.assertEqual(
118 | metrics.TNE_metric(self.models['WITH_CROSSING_POINT']), 2
119 | )
120 |
121 | def testNOAMetricForSimpleModel(self):
122 |
123 | self.assertEqual(
124 | metrics.NOA_metric(self.models['SIMPLE']), 4
125 | )
126 |
127 | def testNOAMetricForComplexModel(self):
128 |
129 | self.assertEqual(
130 | metrics.NOA_metric(self.models['COMPLEX']), 9
131 | )
132 |
133 | def testNOAMetricForModelWithCycles(self):
134 | self.assertEqual(
135 | metrics.NOA_metric(self.models['WITH_CYCLES']), 11
136 | )
137 |
138 | def testNOAMetricForModelWithCrossingPoint(self):
139 | self.assertEqual(
140 | metrics.NOA_metric(self.models['WITH_CROSSING_POINT']), 3
141 | )
142 |
143 | def testNOACMetricForSimpleModel(self):
144 | self.assertEqual(
145 | metrics.NOAC_metric(self.models['SIMPLE']), 8
146 | )
147 |
148 | def testNOACMetricForComplexModel(self):
149 | self.assertEqual(
150 | metrics.NOAC_metric(self.models['COMPLEX']), 21
151 | )
152 |
153 | def testNOACMetricForModelWithCycles(self):
154 | self.assertEqual(
155 | metrics.NOAC_metric(self.models['WITH_CYCLES']), 16
156 | )
157 |
158 | def testNOACMetricForModelWithCrossingPoint(self):
159 | self.assertEqual(
160 | metrics.NOAC_metric(self.models['WITH_CROSSING_POINT']), 7
161 | )
162 |
163 | def testNOAJSMetricForSimpleModel(self):
164 | self.assertEqual(
165 | metrics.NOAJS_metric(self.models['SIMPLE']), 6
166 | )
167 |
168 | def testNOAJSMetricForComplexModel(self):
169 | self.assertEqual(
170 | metrics.NOAJS_metric(self.models['COMPLEX']), 17
171 | )
172 |
173 | def testNOAJSMetricForModelWithCycles(self):
174 | self.assertEqual(
175 | metrics.NOAJS_metric(self.models['WITH_CYCLES']), 13
176 | )
177 |
178 | def testNOAJSMetricForModelWithCrossingPoint(self):
179 | self.assertEqual(
180 | metrics.NOAJS_metric(self.models['WITH_CROSSING_POINT']), 5
181 | )
182 |
183 | def testNumberOfNodesForSimpleModel(self):
184 |
185 | self.assertEqual(
186 | metrics.NumberOfNodes_metric(self.models['SIMPLE']), 8
187 | )
188 |
189 | def testNumberOfNodesForComplexModel(self):
190 |
191 | self.assertEqual(
192 | metrics.NumberOfNodes_metric(self.models['COMPLEX']), 21
193 | )
194 |
195 | def testNumberOfNodesForModelWithCycles(self):
196 | self.assertEqual(
197 | metrics.NumberOfNodes_metric(self.models['WITH_CYCLES']), 16
198 | )
199 |
200 | def testNumberOfNodesForModelWithCrossingPoint(self):
201 | self.assertEqual(
202 | metrics.NumberOfNodes_metric(self.models['WITH_CROSSING_POINT']), 7
203 | )
204 |
205 | def testGatewayHeterogenityMetricForSimpleModel(self):
206 |
207 | self.assertEqual(
208 | metrics.GatewayHeterogenity_metric(self.models['SIMPLE']), 1
209 | )
210 |
211 | def testGatewayHeterogenityMetricForComplexModel(self):
212 |
213 | self.assertEqual(
214 | metrics.GatewayHeterogenity_metric(self.models['COMPLEX']), 4
215 | )
216 |
217 | def testGatewayHeterogenityMetricForModelWithCycles(self):
218 | self.assertEqual(
219 | metrics.GatewayHeterogenity_metric(self.models['WITH_CYCLES']), 1
220 | )
221 |
222 | def testGatewayHeterogenityMetricForModelWithCrossingPoint(self):
223 | self.assertEqual(
224 | metrics.GatewayHeterogenity_metric(self.models['WITH_CROSSING_POINT']), 1
225 | )
226 |
227 | def testCoefficientOfNetworkComplexityMetricForSimpleModel(self):
228 |
229 | self.assertAlmostEqual(
230 | metrics.CoefficientOfNetworkComplexity_metric(self.models['SIMPLE']), 1.0,
231 | places=3
232 | )
233 |
234 | def testCoefficientOfNetworkComplexityMetricForComplexModel(self):
235 |
236 | self.assertAlmostEqual(
237 | metrics.CoefficientOfNetworkComplexity_metric(self.models['COMPLEX']), 1.143,
238 | places=3
239 | )
240 |
241 | def testCoefficientOfNetworkComplexityMetricForModelWithCycles(self):
242 | self.assertAlmostEqual(
243 | metrics.CoefficientOfNetworkComplexity_metric(self.models['WITH_CYCLES']), 1.0,
244 | places=3
245 | )
246 |
247 | def testCoefficientOfNetworkComplexityMetricForModelWithCrossingPoint(self):
248 | self.assertAlmostEqual(
249 | metrics.CoefficientOfNetworkComplexity_metric(self.models['WITH_CROSSING_POINT']), 1.0
250 | )
251 |
252 | def testAverageGatewayDegreeMetricForSimpleModel(self):
253 |
254 | self.assertAlmostEqual(
255 | metrics.AverageGatewayDegree_metric(self.models['SIMPLE']), 3.0,
256 | places=3
257 | )
258 |
259 | def testAverageGatewayDegreeMetricForComplexModel(self):
260 |
261 | self.assertAlmostEqual(
262 | metrics.AverageGatewayDegree_metric(self.models['COMPLEX']), 3.0,
263 | places=3
264 | )
265 |
266 | def testAverageGatewayDegreeForModelWithCycles(self):
267 | self.assertAlmostEqual(
268 | metrics.AverageGatewayDegree_metric(self.models['WITH_CYCLES']), 3.5,
269 | places=3
270 | )
271 |
272 | def testAverageGatewayDegreeForModelWithCrossingPoint(self):
273 | self.assertAlmostEqual(
274 | metrics.AverageGatewayDegree_metric(self.models['WITH_CROSSING_POINT']), 3.0
275 | )
276 |
277 | def testDurfeeSquareMetricForSimpleModel(self):
278 |
279 | self.assertEqual(
280 | metrics.DurfeeSquare_metric(self.models['SIMPLE']), 2
281 | )
282 |
283 | def testDurfeeSquareMetricForComplexModel(self):
284 |
285 | self.assertEqual(
286 | metrics.DurfeeSquare_metric(self.models['COMPLEX']), 2
287 | )
288 |
289 | def testDurfeeSquareForModelWithCycles(self):
290 |
291 | self.assertEqual(
292 | metrics.DurfeeSquare_metric(self.models['WITH_CYCLES']), 2
293 | )
294 |
295 | def testDurfeeSquareForModelWithCrossingPoint(self):
296 | self.assertEqual(
297 | metrics.DurfeeSquare_metric(self.models['WITH_CROSSING_POINT']), 2
298 | )
299 |
300 | def testPerfectSquareMetricForSimpleModel(self):
301 |
302 | self.assertEqual(
303 | metrics.PerfectSquare_metric(self.models['SIMPLE']), 2
304 | )
305 |
306 | def testPerfectSquareMetricForComplexModel(self):
307 |
308 | self.assertEqual(
309 | metrics.PerfectSquare_metric(self.models['COMPLEX']), 3
310 | )
311 |
312 | def testPerfectSquareForModelWithCycles(self):
313 |
314 | self.assertEqual(
315 | metrics.PerfectSquare_metric(self.models['WITH_CYCLES']), 4
316 | )
317 |
318 | def testPerfectSquareForModelWithCrossingPoint(self):
319 | self.assertEqual(
320 | metrics.PerfectSquare_metric(self.models['WITH_CROSSING_POINT']), 2
321 | )
322 |
323 | def tearDown(self):
324 | pass
325 |
326 |
327 | if __name__ == '__main__':
328 | unittest.main()
329 |
--------------------------------------------------------------------------------
/tests/xml_import_export/bpmn_editor_import_test.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Test unit, using simple graph made in BPMNEditor editor for import/export operation
4 | """
5 | import os
6 | import unittest
7 |
8 | import bpmn_python.bpmn_diagram_visualizer as visualizer
9 | import bpmn_python.bpmn_diagram_rep as diagram
10 |
11 |
12 | class BPMNEditorTests(unittest.TestCase):
13 | """
14 | This class contains test for bpmn-python package functionality using an example BPMN diagram created in BPMNEditor.
15 | """
16 | output_directory = "./output/test-bpmneditor/"
17 | example_path = "../examples/xml_import_export/bpmn_editor_simple_example.xml"
18 | output_file_with_di = "BPMNEditor-example-output.xml"
19 | output_file_no_di = "BPMNEditor-example-output-no-di.xml"
20 | output_dot_file = "BPMNEditor-example"
21 | output_png_file = "BPMNEditor-example"
22 |
23 | def test_loadBPMNEditorDiagram(self):
24 | """
25 | Test for importing a simple BPMNEditor diagram example (as BPMN 2.0 XML) into inner representation
26 | and later exporting it to XML file
27 | """
28 | bpmn_graph = diagram.BpmnDiagramGraph()
29 | bpmn_graph.load_diagram_from_xml_file(os.path.abspath(self.example_path))
30 | bpmn_graph.export_xml_file(self.output_directory, self.output_file_with_di)
31 | bpmn_graph.export_xml_file_no_di(self.output_directory, self.output_file_no_di)
32 |
33 | def test_loadBPMNEditorDiagramAndVisualize(self):
34 | """
35 | Test for importing a simple BPMNEditor diagram example (as BPMN 2.0 XML) into inner representation
36 | and later exporting it to XML file. Includes test for visualization functionality.
37 | """
38 | bpmn_graph = diagram.BpmnDiagramGraph()
39 | bpmn_graph.load_diagram_from_xml_file(os.path.abspath(self.example_path))
40 | # Uncomment line below to get a simple view of created diagram
41 | # visualizer.visualize_diagram(bpmn_graph)
42 | visualizer.bpmn_diagram_to_dot_file(bpmn_graph, self.output_directory + self.output_dot_file)
43 | visualizer.bpmn_diagram_to_png(bpmn_graph, self.output_directory + self.output_png_file)
44 | bpmn_graph.export_xml_file(self.output_directory, self.output_file_with_di)
45 | bpmn_graph.export_xml_file_no_di(self.output_directory, self.output_file_no_di)
46 |
47 |
48 | if __name__ == '__main__':
49 | unittest.main()
50 |
--------------------------------------------------------------------------------
/tests/xml_import_export/camunda_complex_test.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Test unit, using more complex graph made in bpmn.io editor for import/export operation
4 | """
5 | import os
6 | import unittest
7 |
8 | import bpmn_python.bpmn_diagram_visualizer as visualizer
9 | import bpmn_python.bpmn_diagram_rep as diagram
10 |
11 |
12 | class CamundaComplexTests(unittest.TestCase):
13 | """
14 | This class contains test for bpmn-python package functionality using a complex example of BPMN diagram
15 | created in bpmn-io (Camunda library implementation).
16 | """
17 | output_directory = "./output/test-camunda/complex/"
18 | example_path = "../examples/xml_import_export/camunda_complex_example.bpmn"
19 | output_file_with_di = "camunda-complex-example-output.xml"
20 | output_file_no_di = "camunda-complex-example-output-no-di.xml"
21 | output_dot_file = "camunda-complex-example"
22 | output_png_file = "camunda-complex-example"
23 |
24 | def test_loadCamundaComplexDiagram(self):
25 | """
26 | Test for importing a complex Camunda diagram example (as BPMN 2.0 XML) into inner representation
27 | and later exporting it to XML file
28 | """
29 | bpmn_graph = diagram.BpmnDiagramGraph()
30 | bpmn_graph.load_diagram_from_xml_file(os.path.abspath(self.example_path))
31 | bpmn_graph.export_xml_file(self.output_directory, self.output_file_with_di)
32 | bpmn_graph.export_xml_file_no_di(self.output_directory, self.output_file_no_di)
33 |
34 | def test_loadCamundaComplexDiagramAndVisualize(self):
35 | """
36 | Test for importing a complex Camunda diagram example (as BPMN 2.0 XML) into inner representation
37 | and later exporting it to XML file. Includes test for visualization functionality.
38 | """
39 | bpmn_graph = diagram.BpmnDiagramGraph()
40 | bpmn_graph.load_diagram_from_xml_file(os.path.abspath(self.example_path))
41 | # Uncomment line below to get a simple view of created diagram
42 | # visualizer.visualize_diagram(bpmn_graph)
43 | visualizer.bpmn_diagram_to_dot_file(bpmn_graph, self.output_directory + self.output_dot_file)
44 | visualizer.bpmn_diagram_to_png(bpmn_graph, self.output_directory + self.output_png_file)
45 | bpmn_graph.export_xml_file(self.output_directory, self.output_file_with_di)
46 | bpmn_graph.export_xml_file_no_di(self.output_directory, self.output_file_no_di)
47 |
48 | if __name__ == '__main__':
49 | unittest.main()
50 |
--------------------------------------------------------------------------------
/tests/xml_import_export/camunda_simple_test.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Test unit, using simple graph made in bpmn.io editor for import/export operation
4 | """
5 | import os
6 | import unittest
7 |
8 | import bpmn_python.bpmn_diagram_visualizer as visualizer
9 | import bpmn_python.bpmn_diagram_rep as diagram
10 |
11 |
12 | class CamundaSimpleTests(unittest.TestCase):
13 | """
14 | This class contains test for bpmn-python package functionality using a simple example of BPMN diagram
15 | created in bpmn-io (Camunda library implementation).
16 | """
17 | output_directory = "./output/test-camunda/simple/"
18 | example_path = "../examples/xml_import_export/camunda_simple_example.bpmn"
19 | output_file_with_di = "camunda-example-output.xml"
20 | output_file_no_di = "camunda-example-output-no-di.xml"
21 | output_dot_file = "camunda-example"
22 | output_png_file = "camunda-example"
23 |
24 | def test_loadCamundaSimpleDiagram(self):
25 | """
26 | Test for importing a simple Camunda diagram example (as BPMN 2.0 XML) into inner representation
27 | and later exporting it to XML file
28 | """
29 | bpmn_graph = diagram.BpmnDiagramGraph()
30 | bpmn_graph.load_diagram_from_xml_file(os.path.abspath(self.example_path))
31 | bpmn_graph.export_xml_file(self.output_directory, self.output_file_with_di)
32 | bpmn_graph.export_xml_file_no_di(self.output_directory, self.output_file_no_di)
33 |
34 | def test_loadCamundaSimpleDiagramAndVisualize(self):
35 | """
36 | Test for importing a simple Camunda diagram example (as BPMN 2.0 XML) into inner representation
37 | and later exporting it to XML file. Includes test for visualization functionality.
38 | """
39 | bpmn_graph = diagram.BpmnDiagramGraph()
40 | bpmn_graph.load_diagram_from_xml_file(os.path.abspath(self.example_path))
41 | # Uncomment line below to get a simple view of created diagram
42 | # visualizer.visualize_diagram(bpmn_graph)
43 | visualizer.bpmn_diagram_to_dot_file(bpmn_graph, self.output_directory + self.output_dot_file)
44 | visualizer.bpmn_diagram_to_png(bpmn_graph, self.output_directory + self.output_png_file)
45 | bpmn_graph.export_xml_file(self.output_directory, self.output_file_with_di)
46 | bpmn_graph.export_xml_file_no_di(self.output_directory, self.output_file_no_di)
47 |
48 | if __name__ == '__main__':
49 | unittest.main()
50 |
--------------------------------------------------------------------------------
/tests/xml_import_export/default_conditional_flow_test.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Test unit, using simple graph made in BPMNEditor editor for import/export operation
4 | """
5 | import os
6 | import unittest
7 |
8 | import bpmn_python.bpmn_diagram_rep as diagram
9 |
10 |
11 | class DefaultConditionalFlowTests(unittest.TestCase):
12 | """
13 | This class contains test for bpmn-python package functionality using an example BPMN diagram created in BPMNEditor.
14 | """
15 | output_directory = "./output/test-flows/"
16 | example_path = "../examples/xml_import_export/default-conditional-flow-example.bpmn"
17 | output_file = "default-conditional-flow-example.bpmn"
18 |
19 | def test_loadBPMNEditorDiagramAndVisualize(self):
20 | """
21 | Test for importing a simple BPMNEditor diagram example (as BPMN 2.0 XML) into inner representation
22 | and later exporting it to XML file. Includes test for visualization functionality.
23 | """
24 | bpmn_graph = diagram.BpmnDiagramGraph()
25 | bpmn_graph.load_diagram_from_xml_file(os.path.abspath(self.example_path))
26 | bpmn_graph.export_xml_file(self.output_directory, self.output_file)
27 |
28 | if __name__ == '__main__':
29 | unittest.main()
30 |
--------------------------------------------------------------------------------
/tests/xml_import_export/lanes_test.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Test unit, using simple graph made in Signavio editor for import/export operation
4 | """
5 | import os
6 | import unittest
7 |
8 | import bpmn_python.bpmn_diagram_rep as diagram
9 |
10 |
11 | class BPMNEditorTests(unittest.TestCase):
12 | """
13 | This class contains test for bpmn-python package functionality using an example BPMN diagram, which contains
14 | multiple pool and lane elements.
15 | """
16 | output_directory = "./output/test-lane/"
17 | example_path = "../examples/xml_import_export/lanes.bpmn"
18 | output_file_with_di = "lanes-example-output.xml"
19 | output_file_no_di = "lanes-example-output-no-di.xml"
20 |
21 | def test_load_lanes_example(self):
22 | """
23 | Test for importing a simple BPMNEditor diagram example (as BPMN 2.0 XML) into inner representation
24 | and later exporting it to XML file
25 | """
26 | bpmn_graph = diagram.BpmnDiagramGraph()
27 | bpmn_graph.load_diagram_from_xml_file(os.path.abspath(self.example_path))
28 | bpmn_graph.export_xml_file(self.output_directory, self.output_file_with_di)
29 | bpmn_graph.export_xml_file_no_di(self.output_directory, self.output_file_no_di)
30 |
31 | if __name__ == '__main__':
32 | unittest.main()
33 |
--------------------------------------------------------------------------------
/tests/xml_import_export/manually_generated_complex_diagram.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Test unit, creates a more complex graph using functions provided by package and exports it to XML and graphic format
4 | """
5 | import unittest
6 |
7 | import bpmn_python.bpmn_diagram_layouter as layouter
8 | import bpmn_python.bpmn_diagram_visualizer as visualizer
9 | import bpmn_python.bpmn_diagram_rep as diagram
10 |
11 |
12 | class ManualGenerationComplexTests(unittest.TestCase):
13 | """
14 | This class contains test for manual diagram generation functionality.
15 | """
16 | output_directory = "./output/test-manual/complex/"
17 | output_file_with_di = "manually-generated-complex-output.xml"
18 | output_file_no_di = "manually-generated-complex-output-no-di.xml"
19 | output_dot_file = "manually-generated-example"
20 | output_png_file = "manually-generated-example"
21 |
22 | def test_create_diagram_manually(self):
23 | bpmn_graph = diagram.BpmnDiagramGraph()
24 | bpmn_graph.create_new_diagram_graph(diagram_name="diagram1")
25 | process_id = bpmn_graph.add_process_to_diagram()
26 |
27 | [start_id, _] = bpmn_graph.add_start_event_to_diagram(process_id, start_event_name="start_event")
28 | [task1_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="First task")
29 | [subprocess1_id, _] = bpmn_graph.add_subprocess_to_diagram(process_id, subprocess_name="Subprocess")
30 | bpmn_graph.add_sequence_flow_to_diagram(process_id, start_id, task1_id)
31 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task1_id, subprocess1_id)
32 |
33 | [parallel_gate_fork_id, _] = bpmn_graph.add_parallel_gateway_to_diagram(process_id,
34 | gateway_name="parallel_gate_fork")
35 | [task1_par_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="task1_par")
36 | [task2_par_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="task2_par")
37 | [parallel_gate_join_id, _] = bpmn_graph.add_parallel_gateway_to_diagram(process_id,
38 | gateway_name="parallel_gate_join")
39 |
40 | bpmn_graph.add_sequence_flow_to_diagram(process_id, subprocess1_id, parallel_gate_fork_id)
41 | bpmn_graph.add_sequence_flow_to_diagram(process_id, parallel_gate_fork_id, task1_par_id)
42 | bpmn_graph.add_sequence_flow_to_diagram(process_id, parallel_gate_fork_id, task2_par_id)
43 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task1_par_id, parallel_gate_join_id)
44 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task2_par_id, parallel_gate_join_id)
45 |
46 | [exclusive_gate_fork_id, _] = bpmn_graph.add_exclusive_gateway_to_diagram(process_id,
47 | gateway_name="exclusive_gate_fork")
48 | [task1_ex_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="task1_ex")
49 | [task2_ex_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="task2_ex")
50 | [exclusive_gate_join_id, _] = bpmn_graph.add_exclusive_gateway_to_diagram(process_id,
51 | gateway_name="exclusive_gate_join")
52 |
53 | bpmn_graph.add_sequence_flow_to_diagram(process_id, parallel_gate_join_id, exclusive_gate_fork_id)
54 | bpmn_graph.add_sequence_flow_to_diagram(process_id, exclusive_gate_fork_id, task1_ex_id)
55 | bpmn_graph.add_sequence_flow_to_diagram(process_id, exclusive_gate_fork_id, task2_ex_id)
56 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task1_ex_id, exclusive_gate_join_id)
57 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task2_ex_id, exclusive_gate_join_id)
58 |
59 | [inclusive_gate_fork_id, _] = bpmn_graph.add_inclusive_gateway_to_diagram(process_id,
60 | gateway_name="inclusive_gate_fork")
61 | [task1_in_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="task1_in")
62 | [task2_in_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="task2_in")
63 | [inclusive_gate_join_id, _] = bpmn_graph.add_inclusive_gateway_to_diagram(process_id,
64 | gateway_name="inclusive_gate_join")
65 |
66 | bpmn_graph.add_sequence_flow_to_diagram(process_id, exclusive_gate_join_id, inclusive_gate_fork_id)
67 | bpmn_graph.add_sequence_flow_to_diagram(process_id, inclusive_gate_fork_id, task1_in_id)
68 | bpmn_graph.add_sequence_flow_to_diagram(process_id, inclusive_gate_fork_id, task2_in_id)
69 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task1_in_id, inclusive_gate_join_id)
70 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task2_in_id, inclusive_gate_join_id)
71 |
72 | [end_id, _] = bpmn_graph.add_end_event_to_diagram(process_id, end_event_name="end_event")
73 | bpmn_graph.add_sequence_flow_to_diagram(process_id, inclusive_gate_join_id, end_id)
74 |
75 | layouter.generate_layout(bpmn_graph)
76 |
77 | bpmn_graph.export_xml_file(self.output_directory, self.output_file_with_di)
78 | bpmn_graph.export_xml_file_no_di(self.output_directory, self.output_file_no_di)
79 | # Uncomment line below to get a simple view of created diagram
80 | # visualizer.visualize_diagram(bpmn_graph)
81 | visualizer.bpmn_diagram_to_dot_file(bpmn_graph, self.output_directory + self.output_dot_file)
82 | visualizer.bpmn_diagram_to_png(bpmn_graph, self.output_directory + self.output_png_file)
83 |
84 | if __name__ == '__main__':
85 | unittest.main()
86 |
--------------------------------------------------------------------------------
/tests/xml_import_export/manually_generated_simple_diagram.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Test unit, creates a simple graph using functions provided by package and exports it to XML and graphic format
4 | """
5 | import unittest
6 |
7 | import bpmn_python.bpmn_diagram_layouter as layouter
8 | import bpmn_python.bpmn_diagram_visualizer as visualizer
9 | import bpmn_python.bpmn_diagram_rep as diagram
10 |
11 |
12 | class ManualGenerationSimpleTests(unittest.TestCase):
13 | """
14 | This class contains test for manual diagram generation functionality.
15 | """
16 | output_directory = "./output/test-manual/simple/"
17 | output_file_with_di = "manually-generated-output.xml"
18 | output_file_no_di = "manually-generated-output-no-di.xml"
19 | output_dot_file = "manually-generated-example"
20 | output_png_file = "manually-generated-example"
21 |
22 | def test_create_diagram_manually(self):
23 | bpmn_graph = diagram.BpmnDiagramGraph()
24 | bpmn_graph.create_new_diagram_graph(diagram_name="diagram1")
25 | process_id = bpmn_graph.add_process_to_diagram()
26 | [start_id, _] = bpmn_graph.add_start_event_to_diagram(process_id, start_event_name="start_event",
27 | start_event_definition="timer")
28 | [task1_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="task1")
29 | bpmn_graph.add_sequence_flow_to_diagram(process_id, start_id, task1_id, "start_to_one")
30 |
31 | [exclusive_gate_fork_id, _] = bpmn_graph.add_exclusive_gateway_to_diagram(process_id,
32 | gateway_name="exclusive_gate_fork")
33 | [task1_ex_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="task1_ex")
34 | [task2_ex_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="task2_ex")
35 | [exclusive_gate_join_id, _] = bpmn_graph.add_exclusive_gateway_to_diagram(process_id,
36 | gateway_name="exclusive_gate_join")
37 |
38 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task1_id, exclusive_gate_fork_id, "one_to_ex_fork")
39 | bpmn_graph.add_sequence_flow_to_diagram(process_id, exclusive_gate_fork_id, task1_ex_id, "ex_fork_to_ex_one")
40 | bpmn_graph.add_sequence_flow_to_diagram(process_id, exclusive_gate_fork_id, task2_ex_id, "ex_fork_to_ex_two")
41 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task1_ex_id, exclusive_gate_join_id, "ex_one_to_ex_join")
42 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task2_ex_id, exclusive_gate_join_id, "ex_two_to_ex_join")
43 |
44 | [task2_id, _] = bpmn_graph.add_task_to_diagram(process_id, task_name="task2")
45 | [end_id, _] = bpmn_graph.add_end_event_to_diagram(process_id, end_event_name="end_event",
46 | end_event_definition="message")
47 | bpmn_graph.add_sequence_flow_to_diagram(process_id, exclusive_gate_join_id, task2_id, "ex_join_to_two")
48 | bpmn_graph.add_sequence_flow_to_diagram(process_id, task2_id, end_id, "two_to_end")
49 |
50 | layouter.generate_layout(bpmn_graph)
51 |
52 | bpmn_graph.export_xml_file(self.output_directory, self.output_file_with_di)
53 | bpmn_graph.export_xml_file_no_di(self.output_directory, self.output_file_no_di)
54 | # Uncomment line below to get a simple view of created diagram
55 | # visualizer.visualize_diagram(bpmn_graph)
56 | visualizer.bpmn_diagram_to_dot_file(bpmn_graph, self.output_directory + self.output_dot_file)
57 | visualizer.bpmn_diagram_to_png(bpmn_graph, self.output_directory + self.output_png_file)
58 |
59 |
60 | if __name__ == '__main__':
61 | unittest.main()
62 |
--------------------------------------------------------------------------------
/tests/xml_import_export/signavio_complex_test.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Test unit, using more complex graph made in Signavio editor for import/export operation
4 | """
5 | import os
6 | import unittest
7 |
8 | import bpmn_python.bpmn_diagram_visualizer as visualizer
9 | import bpmn_python.bpmn_diagram_rep as diagram
10 |
11 |
12 | class SignavioComplexTests(unittest.TestCase):
13 | """
14 | This class contains test for bpmn-python package functionality using a complex example of BPMN diagram
15 | created in Signavio Editor.
16 | """
17 | output_directory = "./output/test-signavio/complex/"
18 | example_path = "../examples/xml_import_export/signavio_complex_example.bpmn"
19 | output_file_with_di = "signavio-complex-example-output.xml"
20 | output_file_no_di = "signavio-complex-example-output-no-di.xml"
21 | output_dot_file = "signavio-complex-example"
22 | output_png_file = "signavio-complex-example"
23 |
24 | def test_loadSignavioComplexDiagram(self):
25 | """
26 | Test for importing a complex Signavio diagram example (as BPMN 2.0 XML) into inner representation
27 | and later exporting it to XML file
28 | """
29 | bpmn_graph = diagram.BpmnDiagramGraph()
30 | bpmn_graph.load_diagram_from_xml_file(os.path.abspath(self.example_path))
31 | bpmn_graph.export_xml_file(self.output_directory, self.output_file_with_di)
32 | bpmn_graph.export_xml_file_no_di(self.output_directory, self.output_file_no_di)
33 |
34 | def test_loadSignavioComplexDiagramAndVisualize(self):
35 | """
36 | Test for importing a complex Signavio diagram example (as BPMN 2.0 XML) into inner representation
37 | and later exporting it to XML file. Includes test for visualization functionality.
38 | """
39 | bpmn_graph = diagram.BpmnDiagramGraph()
40 | bpmn_graph.load_diagram_from_xml_file(os.path.abspath(self.example_path))
41 | # Uncomment line below to get a simple view of created diagram
42 | # visualizer.visualize_diagram(bpmn_graph)
43 | visualizer.bpmn_diagram_to_dot_file(bpmn_graph, self.output_directory + self.output_dot_file)
44 | visualizer.bpmn_diagram_to_png(bpmn_graph, self.output_directory + self.output_png_file)
45 | bpmn_graph.export_xml_file(self.output_directory, self.output_file_with_di)
46 | bpmn_graph.export_xml_file_no_di(self.output_directory, self.output_file_no_di)
47 |
48 | if __name__ == '__main__':
49 | unittest.main()
50 |
--------------------------------------------------------------------------------
/tests/xml_import_export/signavio_simple_test.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | """
3 | Test unit, using simple graph made in Signavio editor for import/export operation
4 | """
5 | import os
6 | import unittest
7 |
8 | import bpmn_python.bpmn_diagram_visualizer as visualizer
9 | import bpmn_python.bpmn_diagram_rep as diagram
10 |
11 |
12 | class SignavioSimpleTests(unittest.TestCase):
13 | """
14 | This class contains test for bpmn-python package functionality using a simple example of BPMN diagram
15 | created in Signavio Editor.
16 | """
17 | output_directory = "./output/test-signavio/simple/"
18 | example_path = "../examples/xml_import_export/signavio_simple_example.bpmn"
19 | output_file_with_di = "signavio-example-output.xml"
20 | output_file_no_di = "signavio-example-output-no-di.xml"
21 | output_dot_file = "signavio-example"
22 | output_png_file = "signavio-example"
23 |
24 | def test_loadSignavioSimpleDiagram(self):
25 | """
26 | Test for importing a simple Signavio diagram example (as BPMN 2.0 XML) into inner representation
27 | and later exporting it to XML file
28 | """
29 | bpmn_graph = diagram.BpmnDiagramGraph()
30 | bpmn_graph.load_diagram_from_xml_file(os.path.abspath(self.example_path))
31 | bpmn_graph.export_xml_file(self.output_directory, self.output_file_with_di)
32 | bpmn_graph.export_xml_file_no_di(self.output_directory, self.output_file_no_di)
33 |
34 | def test_loadSignavioSimpleDiagramAndVisualize(self):
35 | """
36 | Test for importing a simple Signavio diagram example (as BPMN 2.0 XML) into inner representation
37 | and later exporting it to XML file. Includes test for visualization functionality.
38 | """
39 | bpmn_graph = diagram.BpmnDiagramGraph()
40 | bpmn_graph.load_diagram_from_xml_file(os.path.abspath(self.example_path))
41 | # Uncomment line below to get a simple view of created diagram
42 | # visualizer.visualize_diagram(bpmn_graph)
43 | visualizer.bpmn_diagram_to_dot_file(bpmn_graph, self.output_directory + self.output_dot_file)
44 | visualizer.bpmn_diagram_to_png(bpmn_graph, self.output_directory + self.output_png_file)
45 | bpmn_graph.export_xml_file(self.output_directory, self.output_file_with_di)
46 | bpmn_graph.export_xml_file_no_di(self.output_directory, self.output_file_no_di)
47 |
48 | if __name__ == '__main__':
49 | unittest.main()
50 |
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | envlist = py27,py35,py36,py37
3 |
4 | [testenv]
5 | deps =
6 | pytest
7 | -rrequirements.txt
8 | commands =
9 | pytest
10 |
--------------------------------------------------------------------------------