openprocess 0.7.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. cpnpy/__init__.py +46 -0
  2. openprocess/__init__.py +57 -0
  3. openprocess/analysis/__init__.py +0 -0
  4. openprocess/analysis/state_space.py +521 -0
  5. openprocess/analysis/state_space_process.py +251 -0
  6. openprocess/cli.py +742 -0
  7. openprocess/exercises/1 Petri nets/Exercise 1.1 Order handling/answer.pnml +31 -0
  8. openprocess/exercises/1 Petri nets/Exercise 1.1 Order handling/question.md +38 -0
  9. openprocess/exercises/2 Soundness/Exercise 2.1 Spot the flaw/answer.pnml +27 -0
  10. openprocess/exercises/2 Soundness/Exercise 2.1 Spot the flaw/net.pnml +29 -0
  11. openprocess/exercises/2 Soundness/Exercise 2.1 Spot the flaw/question.md +65 -0
  12. openprocess/exercises/3 Discovery/Exercise 3.1 The alpha-algorithm/log.txt +1 -0
  13. openprocess/exercises/3 Discovery/Exercise 3.1 The alpha-algorithm/question.md +67 -0
  14. openprocess/exercises/4 Regions/Exercise 4.1 Regions of a transition system/question.md +75 -0
  15. openprocess/exercises/4 Regions/Exercise 4.1 Regions of a transition system/ts.txt +5 -0
  16. openprocess/exercises/5 Markings/Exercise 5.1 Markings and matrices/net.pnml +27 -0
  17. openprocess/exercises/5 Markings/Exercise 5.1 Markings and matrices/question.md +65 -0
  18. openprocess/exercises/6 Inductive Miner/Exercise 6.1 Cuts and trees/log.txt +1 -0
  19. openprocess/exercises/6 Inductive Miner/Exercise 6.1 Cuts and trees/question.md +62 -0
  20. openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/log.txt +1 -0
  21. openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/m1.pnml +36 -0
  22. openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/m2.pnml +28 -0
  23. openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/m3.pnml +30 -0
  24. openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/net.pnml +36 -0
  25. openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/question.md +69 -0
  26. openprocess/exercises/pack.md +14 -0
  27. openprocess/flow/__init__.py +50 -0
  28. openprocess/flow/box.py +466 -0
  29. openprocess/flow/boxes/__init__.py +7 -0
  30. openprocess/flow/boxes/check.py +119 -0
  31. openprocess/flow/boxes/compare.py +16 -0
  32. openprocess/flow/boxes/cpn.py +53 -0
  33. openprocess/flow/boxes/discover.py +124 -0
  34. openprocess/flow/boxes/filter.py +80 -0
  35. openprocess/flow/boxes/input.py +124 -0
  36. openprocess/flow/boxes/output.py +52 -0
  37. openprocess/flow/boxes/predict.py +186 -0
  38. openprocess/flow/boxes/science.py +159 -0
  39. openprocess/flow/boxes/sweeps.py +18 -0
  40. openprocess/flow/convert.py +187 -0
  41. openprocess/flow/datasets.py +198 -0
  42. openprocess/flow/explain.py +115 -0
  43. openprocess/flow/library.py +222 -0
  44. openprocess/flow/record.py +385 -0
  45. openprocess/flow/runner.py +357 -0
  46. openprocess/flow/sweep.py +92 -0
  47. openprocess/flow/types.py +290 -0
  48. openprocess/flow/workflow.py +628 -0
  49. openprocess/gui/__init__.py +0 -0
  50. openprocess/gui/app.py +90 -0
  51. openprocess/gui/arc_editing.py +295 -0
  52. openprocess/gui/canvas.py +1414 -0
  53. openprocess/gui/flow/__init__.py +8 -0
  54. openprocess/gui/flow/canvas.py +854 -0
  55. openprocess/gui/flow/page.py +972 -0
  56. openprocess/gui/flow/templates.py +131 -0
  57. openprocess/gui/flow/viewers.py +665 -0
  58. openprocess/gui/items.py +1275 -0
  59. openprocess/gui/learn/answer_boxes.py +978 -0
  60. openprocess/gui/learn/concealment.py +91 -0
  61. openprocess/gui/learn/mode.py +1181 -0
  62. openprocess/gui/panning.py +241 -0
  63. openprocess/gui/resources/openprocess-icon.png +0 -0
  64. openprocess/gui/studio/__init__.py +1 -0
  65. openprocess/gui/studio/__main__.py +3 -0
  66. openprocess/gui/studio/app.py +4031 -0
  67. openprocess/gui/studio/charts.py +115 -0
  68. openprocess/gui/studio/compare_page.py +487 -0
  69. openprocess/gui/studio/cpn_page.py +1858 -0
  70. openprocess/gui/studio/definition_view.py +284 -0
  71. openprocess/gui/studio/derivation_view.py +421 -0
  72. openprocess/gui/studio/documents.py +152 -0
  73. openprocess/gui/studio/dotted_chart.py +1401 -0
  74. openprocess/gui/studio/file_dialogs.py +143 -0
  75. openprocess/gui/studio/filter_dialog.py +247 -0
  76. openprocess/gui/studio/graph_builders.py +176 -0
  77. openprocess/gui/studio/graph_view.py +682 -0
  78. openprocess/gui/studio/instances.py +413 -0
  79. openprocess/gui/studio/log_editor.py +675 -0
  80. openprocess/gui/studio/log_page.py +800 -0
  81. openprocess/gui/studio/markdown_view.py +127 -0
  82. openprocess/gui/studio/mathtext.py +260 -0
  83. openprocess/gui/studio/ml_highlighter.py +75 -0
  84. openprocess/gui/studio/model_page.py +760 -0
  85. openprocess/gui/studio/net_comparison.py +124 -0
  86. openprocess/gui/studio/notes_overlay.py +275 -0
  87. openprocess/gui/studio/petri_page.py +844 -0
  88. openprocess/gui/studio/regions_view.py +502 -0
  89. openprocess/gui/studio/sidebar.py +149 -0
  90. openprocess/gui/studio/style.py +503 -0
  91. openprocess/gui/studio/tool_icons.py +134 -0
  92. openprocess/gui/studio/updates.py +439 -0
  93. openprocess/gui/studio/widgets.py +899 -0
  94. openprocess/gui/studio/workers.py +60 -0
  95. openprocess/gui/studio/workspace.py +447 -0
  96. openprocess/gui/theme.py +394 -0
  97. openprocess/gui/tidy.py +86 -0
  98. openprocess/io/__init__.py +0 -0
  99. openprocess/io/cpn_reader.py +389 -0
  100. openprocess/io/cpn_writer.py +357 -0
  101. openprocess/learn/__init__.py +23 -0
  102. openprocess/learn/answers.py +188 -0
  103. openprocess/learn/checks.py +953 -0
  104. openprocess/learn/computed.py +1180 -0
  105. openprocess/learn/context.py +145 -0
  106. openprocess/learn/exam.py +169 -0
  107. openprocess/learn/exercise-packs.md +325 -0
  108. openprocess/learn/importer.py +216 -0
  109. openprocess/learn/notation.py +474 -0
  110. openprocess/learn/pack.py +511 -0
  111. openprocess/learn/sheet.py +296 -0
  112. openprocess/mining/__init__.py +73 -0
  113. openprocess/mining/analysis.py +689 -0
  114. openprocess/mining/columns.py +282 -0
  115. openprocess/mining/compare_nets.py +246 -0
  116. openprocess/mining/conformance/__init__.py +0 -0
  117. openprocess/mining/conformance/alignments.py +263 -0
  118. openprocess/mining/conformance/quality.py +145 -0
  119. openprocess/mining/conformance/token_replay.py +252 -0
  120. openprocess/mining/csv_import.py +222 -0
  121. openprocess/mining/definitions.py +584 -0
  122. openprocess/mining/dfg.py +187 -0
  123. openprocess/mining/discovery/__init__.py +0 -0
  124. openprocess/mining/discovery/alpha.py +168 -0
  125. openprocess/mining/discovery/heuristics.py +332 -0
  126. openprocess/mining/discovery/inductive.py +477 -0
  127. openprocess/mining/discovery/state_regions.py +62 -0
  128. openprocess/mining/filtering.py +237 -0
  129. openprocess/mining/footprint.py +183 -0
  130. openprocess/mining/invariants.py +191 -0
  131. openprocess/mining/layout.py +279 -0
  132. openprocess/mining/log.py +364 -0
  133. openprocess/mining/petrinet.py +354 -0
  134. openprocess/mining/playout.py +75 -0
  135. openprocess/mining/pm4py_bridge.py +82 -0
  136. openprocess/mining/pnml.py +223 -0
  137. openprocess/mining/processtree.py +216 -0
  138. openprocess/mining/regions.py +476 -0
  139. openprocess/mining/stats.py +160 -0
  140. openprocess/mining/structure.py +374 -0
  141. openprocess/mining/transition_system.py +409 -0
  142. openprocess/mining/xes.py +399 -0
  143. openprocess/ml/__init__.py +0 -0
  144. openprocess/ml/ast_nodes.py +332 -0
  145. openprocess/ml/builtins.py +364 -0
  146. openprocess/ml/colorsets.py +522 -0
  147. openprocess/ml/errors.py +60 -0
  148. openprocess/ml/evaluator.py +754 -0
  149. openprocess/ml/lexer.py +277 -0
  150. openprocess/ml/multiset.py +417 -0
  151. openprocess/ml/parser.py +737 -0
  152. openprocess/ml/values.py +319 -0
  153. openprocess/model/__init__.py +0 -0
  154. openprocess/model/declarations.py +617 -0
  155. openprocess/model/examples.py +98 -0
  156. openprocess/model/net.py +701 -0
  157. openprocess/model/plain.py +192 -0
  158. openprocess/references.py +280 -0
  159. openprocess/sim/__init__.py +0 -0
  160. openprocess/sim/binding.py +620 -0
  161. openprocess/sim/export.py +66 -0
  162. openprocess/sim/simulator.py +315 -0
  163. openprocess/teaching/__init__.py +4 -0
  164. openprocess/teaching/answers.py +4 -0
  165. openprocess/teaching/checks.py +5 -0
  166. openprocess/teaching/pack.py +4 -0
  167. openprocess/teaching/sheet.py +4 -0
  168. openprocess-0.7.0.dist-info/METADATA +927 -0
  169. openprocess-0.7.0.dist-info/RECORD +173 -0
  170. openprocess-0.7.0.dist-info/WHEEL +5 -0
  171. openprocess-0.7.0.dist-info/entry_points.txt +6 -0
  172. openprocess-0.7.0.dist-info/licenses/LICENSE +21 -0
  173. openprocess-0.7.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,241 @@
1
+ """An endless canvas: pan anywhere, zoom about the pointer.
2
+
3
+ Earlier versions sized the scrollable area to the drawing. With the whole
4
+ net in view there was then nothing to scroll: two-finger scrolling did
5
+ nothing, zooming could not keep the point under the pointer still (the view
6
+ re-centred the drawing instead), and to start a new part of the net beside
7
+ the old one you first had to zoom in. Middle-drag panning grew the area on
8
+ the fly, but only that one gesture did.
9
+
10
+ This mixin gives the view a very large scene rect and no scroll bars, so the
11
+ canvas behaves like a sheet of paper that goes on in every direction:
12
+
13
+ * two-finger scroll (or the mouse wheel; Shift + wheel goes sideways) pans,
14
+ whatever is in view;
15
+ * dragging with the middle button, or with the left button while Space is
16
+ held, pans too -- whatever the tool or the item under the pointer;
17
+ * ⌘/Ctrl + scroll and trackpad pinch zoom about the pointer
18
+ (:meth:`CanvasPanning.zoom_about`).
19
+
20
+ Zooming to fit just frames the drawing; nothing has to be reset.
21
+
22
+ The drawing can never be lost, but it can be panned out of view: then a
23
+ hint at the top of the canvas says so, with an arrow pointing to where it
24
+ is, and clicking it frames the drawing again (as *Fit* does).
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ import math
30
+
31
+ from PySide6.QtCore import QPointF, QRectF, Qt, QTimer
32
+ from PySide6.QtGui import QCursor
33
+ from PySide6.QtWidgets import QGraphicsView, QPushButton
34
+
35
+ #: Arrows pointing from the middle of the view towards the drawing, by
36
+ #: eighths of a turn, starting east and going clockwise (screen y points down).
37
+ ARROWS = "→↘↓↙←↖↑↗"
38
+
39
+
40
+ #: Half the side of the canvas, in scene units: far more than any net needs,
41
+ #: small enough that the scroll range fits in an int at the largest zoom.
42
+ EXTENT = 50_000.0
43
+
44
+
45
+ class CanvasPanning:
46
+ """Mix into a ``QGraphicsView`` subclass (before it in the bases) and call
47
+ :meth:`init_canvas` at the end of ``__init__``."""
48
+
49
+ _pan_last = None # the pointer position while a pan drag is on
50
+ _pan_cursor = None # the (tool's) cursor to put back afterwards
51
+ _space_held = False # Space is down: a left drag pans
52
+ _space_cursor = None
53
+ #: What the off-screen hint calls the drawing.
54
+ drawing_name = "The drawing"
55
+
56
+ def init_canvas(self) -> None:
57
+ self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
58
+ self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
59
+ self.setSceneRect(QRectF(-EXTENT, -EXTENT, 2 * EXTENT, 2 * EXTENT))
60
+ # The anchor for zooming needs to know where the pointer is.
61
+ self.viewport().setMouseTracking(True)
62
+ # The hint shown when the drawing is out of view.
63
+ self.offscreen_hint = QPushButton(self)
64
+ self.offscreen_hint.setObjectName("offscreenHint")
65
+ self.offscreen_hint.setCursor(Qt.PointingHandCursor)
66
+ self.offscreen_hint.setToolTip("Frame the whole drawing again (Fit)")
67
+ self.offscreen_hint.clicked.connect(self.show_drawing)
68
+ self.offscreen_hint.hide()
69
+ self._hint_timer = QTimer(self)
70
+ self._hint_timer.setSingleShot(True)
71
+ self._hint_timer.setInterval(30)
72
+ self._hint_timer.timeout.connect(self.update_offscreen_hint)
73
+ self.horizontalScrollBar().valueChanged.connect(lambda _value: self._hint_timer.start())
74
+ self.verticalScrollBar().valueChanged.connect(lambda _value: self._hint_timer.start())
75
+ if self.scene() is not None:
76
+ self.scene().changed.connect(lambda _rects: self._hint_timer.start())
77
+
78
+ # -- the drawing out of view ------------------------------------------------------
79
+ def show_drawing(self) -> None:
80
+ """Frame the whole drawing (the view's own Fit)."""
81
+ fit = getattr(self, "zoom_to_fit", None) or getattr(self, "fit", None)
82
+ if fit is not None:
83
+ fit()
84
+ self.update_offscreen_hint()
85
+
86
+ def update_offscreen_hint(self) -> None:
87
+ """Show the hint, pointing the right way, while none of the drawing is in view."""
88
+ hint = getattr(self, "offscreen_hint", None)
89
+ if hint is None or self.scene() is None:
90
+ return
91
+ drawing = self.scene().itemsBoundingRect()
92
+ visible = self.mapToScene(self.viewport().rect()).boundingRect()
93
+ if drawing.isEmpty() or visible.intersects(drawing):
94
+ hint.hide()
95
+ return
96
+ offset = self.mapFromScene(drawing.center()) - self.viewport().rect().center()
97
+ angle = math.degrees(math.atan2(offset.y(), offset.x())) % 360
98
+ arrow = ARROWS[round(angle / 45) % 8]
99
+ hint.setText(f"{arrow} {self.drawing_name} is out of view — show it")
100
+ hint.adjustSize()
101
+ area = self.viewport().geometry()
102
+ hint.move(area.center().x() - hint.width() // 2, area.top() + 12)
103
+ hint.show()
104
+ hint.raise_()
105
+
106
+ def resizeEvent(self, event) -> None: # noqa: N802
107
+ super().resizeEvent(event)
108
+ timer = getattr(self, "_hint_timer", None)
109
+ if timer is not None:
110
+ timer.start()
111
+
112
+ # -- dragging -------------------------------------------------------------------
113
+ def _starts_pan(self, event) -> bool:
114
+ return event.button() == Qt.MiddleButton or (
115
+ event.button() == Qt.LeftButton and self._space_held)
116
+
117
+ def mousePressEvent(self, event) -> None: # noqa: N802
118
+ if self._starts_pan(event):
119
+ self._pan_last = event.position()
120
+ self._pan_button = event.button()
121
+ if self._pan_cursor is None:
122
+ self._pan_cursor = self.viewport().cursor()
123
+ self.viewport().setCursor(Qt.ClosedHandCursor)
124
+ event.accept()
125
+ return
126
+ super().mousePressEvent(event)
127
+
128
+ def mouseMoveEvent(self, event) -> None: # noqa: N802
129
+ if self._pan_last is not None:
130
+ if not event.buttons() & self._pan_button: # released outside the window
131
+ self._end_pan()
132
+ else:
133
+ delta = event.position() - self._pan_last
134
+ self._pan_last = event.position()
135
+ self.pan_by(round(delta.x()), round(delta.y()))
136
+ event.accept()
137
+ return
138
+ super().mouseMoveEvent(event)
139
+
140
+ def mouseReleaseEvent(self, event) -> None: # noqa: N802
141
+ if self._pan_last is not None and event.button() == self._pan_button:
142
+ self._end_pan()
143
+ event.accept()
144
+ return
145
+ super().mouseReleaseEvent(event)
146
+
147
+ def _end_pan(self) -> None:
148
+ self._pan_last = None
149
+ if self._space_held:
150
+ self.viewport().setCursor(Qt.OpenHandCursor)
151
+ else:
152
+ self._restore_cursor()
153
+
154
+ def _restore_cursor(self) -> None:
155
+ if self._pan_cursor is not None:
156
+ self.viewport().setCursor(self._pan_cursor)
157
+ self._pan_cursor = None
158
+
159
+ def keyPressEvent(self, event) -> None: # noqa: N802
160
+ if event.key() == Qt.Key_Space and not event.isAutoRepeat() \
161
+ and event.modifiers() == Qt.NoModifier:
162
+ self._space_held = True
163
+ if self._pan_cursor is None:
164
+ self._pan_cursor = self.viewport().cursor()
165
+ self.viewport().setCursor(Qt.OpenHandCursor)
166
+ event.accept()
167
+ return
168
+ if event.key() == Qt.Key_Space:
169
+ event.accept() # auto-repeat while held
170
+ return
171
+ super().keyPressEvent(event)
172
+
173
+ def keyReleaseEvent(self, event) -> None: # noqa: N802
174
+ if event.key() == Qt.Key_Space and not event.isAutoRepeat():
175
+ self._space_held = False
176
+ if self._pan_last is None:
177
+ self._restore_cursor()
178
+ event.accept()
179
+ return
180
+ super().keyReleaseEvent(event)
181
+
182
+ def focusOutEvent(self, event) -> None: # noqa: N802
183
+ if self._space_held: # Space released somewhere else
184
+ self._space_held = False
185
+ if self._pan_last is None:
186
+ self._restore_cursor()
187
+ super().focusOutEvent(event)
188
+
189
+ # -- moving the view ----------------------------------------------------------------
190
+ def pan_by(self, dx: int, dy: int) -> None:
191
+ """Move the drawing ``dx``, ``dy`` pixels (with the pointer)."""
192
+ # Panning is taking charge of the view, like zooming by hand: resizing
193
+ # the window must not re-fit it.
194
+ self.auto_fit = False
195
+ horizontal, vertical = self.horizontalScrollBar(), self.verticalScrollBar()
196
+ horizontal.setValue(horizontal.value() - dx)
197
+ vertical.setValue(vertical.value() - dy)
198
+
199
+ def wheel_pan(self, event) -> None:
200
+ """Scroll: pan by the trackpad's pixels, or a step per wheel notch."""
201
+ pixels = event.pixelDelta()
202
+ if not pixels.isNull():
203
+ dx, dy = pixels.x(), pixels.y()
204
+ else:
205
+ angle = event.angleDelta()
206
+ dx, dy = angle.x() / 3, angle.y() / 3 # 40 px per notch
207
+ if event.modifiers() & Qt.ShiftModifier and not dx:
208
+ dx, dy = dy, 0 # Shift + wheel: sideways
209
+ self.pan_by(round(dx), round(dy))
210
+ event.accept()
211
+
212
+ def zoom_about(self, ratio: float, point: QPointF | None = None) -> None:
213
+ """Scale by ``ratio`` keeping the scene point under ``point`` (viewport
214
+ coordinates; the pointer, else the centre) where it is on screen."""
215
+ if point is None:
216
+ pointer = self.viewport().mapFromGlobal(QCursor.pos())
217
+ point = QPointF(pointer if self.viewport().rect().contains(pointer)
218
+ else self.viewport().rect().center())
219
+ anchor = self.transformationAnchor()
220
+ self.setTransformationAnchor(QGraphicsView.NoAnchor)
221
+ before = self.mapToScene(point.toPoint())
222
+ self.scale(ratio, ratio)
223
+ if getattr(self, "_hint_timer", None) is not None:
224
+ self._hint_timer.start()
225
+ after = self.mapFromScene(before)
226
+ self.horizontalScrollBar().setValue(self.horizontalScrollBar().value()
227
+ + after.x() - round(point.x()))
228
+ self.verticalScrollBar().setValue(self.verticalScrollBar().value()
229
+ + after.y() - round(point.y()))
230
+ self.setTransformationAnchor(anchor)
231
+
232
+ # Older names, kept for callers outside this module.
233
+ def reset_pan_area(self) -> None:
234
+ """Nothing to reset: the canvas is endless."""
235
+
236
+ def hold_still(self) -> None:
237
+ """The drawing never moves by itself on an endless canvas."""
238
+
239
+
240
+ #: The previous name of the mixin.
241
+ MiddleButtonPan = CanvasPanning
@@ -0,0 +1 @@
1
+ """OpenProcess Studio: the process-mining workspace (logs, discovery, conformance)."""
@@ -0,0 +1,3 @@
1
+ from .app import main
2
+
3
+ raise SystemExit(main())