openprocess 0.11.0__tar.gz → 0.12.0__tar.gz

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 (237) hide show
  1. {openprocess-0.11.0/openprocess.egg-info → openprocess-0.12.0}/PKG-INFO +73 -29
  2. {openprocess-0.11.0 → openprocess-0.12.0}/README.md +72 -28
  3. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/__init__.py +1 -1
  4. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/cli.py +23 -4
  5. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/flow/box.py +85 -29
  6. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/flow/boxes/check.py +46 -15
  7. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/flow/boxes/filter.py +56 -7
  8. openprocess-0.12.0/openprocess/flow/boxes/measure.py +195 -0
  9. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/flow/boxes/predict.py +14 -8
  10. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/flow/boxes/science.py +102 -9
  11. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/flow/convert.py +83 -10
  12. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/flow/figures.py +14 -1
  13. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/flow/library.py +3 -3
  14. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/flow/record.py +256 -18
  15. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/flow/report.py +2 -2
  16. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/flow/reproducibility.py +9 -1
  17. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/flow/runner.py +75 -7
  18. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/flow/script.py +1 -1
  19. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/flow/snapshots.py +9 -6
  20. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/flow/workflow.py +25 -10
  21. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/canvas.py +10 -5
  22. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/flow/canvas.py +6 -1
  23. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/flow/page.py +110 -38
  24. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/flow/picker.py +114 -89
  25. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/flow/summary.py +3 -3
  26. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/flow/templates.py +4 -3
  27. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/flow/viewers.py +39 -11
  28. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/learn/mode.py +1 -0
  29. openprocess-0.12.0/openprocess/gui/studio/alignment_view.py +235 -0
  30. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/app.py +89 -23
  31. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/cpn_page.py +55 -25
  32. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/derivation_view.py +3 -1
  33. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/dotted_chart.py +273 -80
  34. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/graph_view.py +52 -0
  35. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/log_page.py +83 -28
  36. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/model_page.py +35 -52
  37. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/petri_page.py +13 -9
  38. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/sidebar.py +6 -4
  39. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/tool_icons.py +63 -20
  40. openprocess-0.12.0/openprocess/mining/cases.py +86 -0
  41. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/columns.py +78 -3
  42. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/compare_nets.py +58 -0
  43. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/conformance/alignments.py +86 -7
  44. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/conformance/quality.py +54 -43
  45. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/conformance/token_replay.py +91 -5
  46. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/discovery/alpha.py +2 -2
  47. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/discovery/heuristics.py +21 -13
  48. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/discovery/inductive.py +133 -24
  49. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/filtering.py +32 -22
  50. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/layout.py +43 -13
  51. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/log.py +8 -1
  52. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/stats.py +50 -0
  53. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/xes.py +136 -56
  54. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/references.py +5 -2
  55. {openprocess-0.11.0 → openprocess-0.12.0/openprocess.egg-info}/PKG-INFO +73 -29
  56. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess.egg-info/SOURCES.txt +7 -0
  57. openprocess-0.12.0/tests/test_docs.py +16 -0
  58. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_flow.py +21 -1
  59. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_flow_gui.py +113 -8
  60. openprocess-0.12.0/tests/test_framework_contract.py +311 -0
  61. openprocess-0.12.0/tests/test_large_logs.py +179 -0
  62. openprocess-0.12.0/tests/test_measure.py +156 -0
  63. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_mining.py +71 -0
  64. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_studio_gui.py +94 -3
  65. {openprocess-0.11.0 → openprocess-0.12.0}/LICENSE +0 -0
  66. {openprocess-0.11.0 → openprocess-0.12.0}/cpnpy/__init__.py +0 -0
  67. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/analysis/__init__.py +0 -0
  68. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/analysis/state_space.py +0 -0
  69. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/analysis/state_space_process.py +0 -0
  70. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/citation.py +0 -0
  71. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/exercises/1 Petri nets/Exercise 1.1 Order handling/answer.pnml +0 -0
  72. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/exercises/1 Petri nets/Exercise 1.1 Order handling/question.md +0 -0
  73. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/exercises/2 Soundness/Exercise 2.1 Spot the flaw/answer.pnml +0 -0
  74. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/exercises/2 Soundness/Exercise 2.1 Spot the flaw/net.pnml +0 -0
  75. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/exercises/2 Soundness/Exercise 2.1 Spot the flaw/question.md +0 -0
  76. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/exercises/3 Discovery/Exercise 3.1 The alpha-algorithm/log.txt +0 -0
  77. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/exercises/3 Discovery/Exercise 3.1 The alpha-algorithm/question.md +0 -0
  78. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/exercises/4 Regions/Exercise 4.1 Regions of a transition system/question.md +0 -0
  79. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/exercises/4 Regions/Exercise 4.1 Regions of a transition system/ts.txt +0 -0
  80. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/exercises/5 Markings/Exercise 5.1 Markings and matrices/net.pnml +0 -0
  81. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/exercises/5 Markings/Exercise 5.1 Markings and matrices/question.md +0 -0
  82. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/exercises/6 Inductive Miner/Exercise 6.1 Cuts and trees/log.txt +0 -0
  83. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/exercises/6 Inductive Miner/Exercise 6.1 Cuts and trees/question.md +0 -0
  84. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/log.txt +0 -0
  85. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/m1.pnml +0 -0
  86. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/m2.pnml +0 -0
  87. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/m3.pnml +0 -0
  88. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/net.pnml +0 -0
  89. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/question.md +0 -0
  90. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/exercises/pack.md +0 -0
  91. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/flow/__init__.py +0 -0
  92. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/flow/boxes/__init__.py +0 -0
  93. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/flow/boxes/compare.py +0 -0
  94. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/flow/boxes/cpn.py +0 -0
  95. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/flow/boxes/discover.py +0 -0
  96. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/flow/boxes/input.py +0 -0
  97. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/flow/boxes/output.py +0 -0
  98. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/flow/boxes/sweeps.py +0 -0
  99. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/flow/datasets.py +0 -0
  100. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/flow/explain.py +0 -0
  101. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/flow/history.py +0 -0
  102. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/flow/provenance.py +0 -0
  103. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/flow/sweep.py +0 -0
  104. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/flow/types.py +0 -0
  105. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/__init__.py +0 -0
  106. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/app.py +0 -0
  107. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/arc_editing.py +0 -0
  108. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/flow/__init__.py +0 -0
  109. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/flow/runs.py +0 -0
  110. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/flow/stepper.py +0 -0
  111. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/items.py +0 -0
  112. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/learn/answer_boxes.py +0 -0
  113. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/learn/concealment.py +0 -0
  114. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/panning.py +0 -0
  115. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/resources/openprocess-icon.png +0 -0
  116. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/__init__.py +0 -0
  117. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/__main__.py +0 -0
  118. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/charts.py +0 -0
  119. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/cite.py +0 -0
  120. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/command_palette.py +0 -0
  121. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/compare_page.py +0 -0
  122. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/connections.py +0 -0
  123. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/definition_view.py +0 -0
  124. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/documents.py +0 -0
  125. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/file_dialogs.py +0 -0
  126. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/filter_dialog.py +0 -0
  127. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/graph_builders.py +0 -0
  128. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/instances.py +0 -0
  129. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/log_editor.py +0 -0
  130. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/markdown_view.py +0 -0
  131. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/mathtext.py +0 -0
  132. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/ml_highlighter.py +0 -0
  133. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/motion.py +0 -0
  134. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/net_comparison.py +0 -0
  135. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/notes_overlay.py +0 -0
  136. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/provenance.py +0 -0
  137. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/regions_view.py +0 -0
  138. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/spaces.py +0 -0
  139. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/style.py +0 -0
  140. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/updates.py +0 -0
  141. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/widgets.py +0 -0
  142. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/workers.py +0 -0
  143. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/studio/workspace.py +0 -0
  144. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/theme.py +0 -0
  145. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/gui/tidy.py +0 -0
  146. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/io/__init__.py +0 -0
  147. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/io/cpn_reader.py +0 -0
  148. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/io/cpn_writer.py +0 -0
  149. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/learn/__init__.py +0 -0
  150. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/learn/answers.py +0 -0
  151. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/learn/checks.py +0 -0
  152. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/learn/computed.py +0 -0
  153. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/learn/context.py +0 -0
  154. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/learn/exam.py +0 -0
  155. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/learn/exercise-packs.md +0 -0
  156. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/learn/importer.py +0 -0
  157. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/learn/notation.py +0 -0
  158. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/learn/pack.py +0 -0
  159. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/learn/sheet.py +0 -0
  160. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/__init__.py +0 -0
  161. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/analysis.py +0 -0
  162. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/conformance/__init__.py +0 -0
  163. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/csv_import.py +0 -0
  164. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/definitions.py +0 -0
  165. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/dfg.py +0 -0
  166. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/discovery/__init__.py +0 -0
  167. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/discovery/state_regions.py +0 -0
  168. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/footprint.py +0 -0
  169. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/invariants.py +0 -0
  170. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/ocel.py +0 -0
  171. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/petrinet.py +0 -0
  172. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/playout.py +0 -0
  173. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/pm4py_bridge.py +0 -0
  174. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/pnml.py +0 -0
  175. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/processtree.py +0 -0
  176. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/regions.py +0 -0
  177. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/structure.py +0 -0
  178. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/mining/transition_system.py +0 -0
  179. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/ml/__init__.py +0 -0
  180. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/ml/ast_nodes.py +0 -0
  181. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/ml/builtins.py +0 -0
  182. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/ml/colorsets.py +0 -0
  183. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/ml/errors.py +0 -0
  184. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/ml/evaluator.py +0 -0
  185. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/ml/lexer.py +0 -0
  186. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/ml/multiset.py +0 -0
  187. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/ml/parser.py +0 -0
  188. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/ml/values.py +0 -0
  189. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/model/__init__.py +0 -0
  190. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/model/declarations.py +0 -0
  191. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/model/examples.py +0 -0
  192. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/model/net.py +0 -0
  193. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/model/plain.py +0 -0
  194. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/packages.py +0 -0
  195. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/sim/__init__.py +0 -0
  196. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/sim/binding.py +0 -0
  197. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/sim/export.py +0 -0
  198. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/sim/simulator.py +0 -0
  199. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/teaching/__init__.py +0 -0
  200. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/teaching/answers.py +0 -0
  201. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/teaching/checks.py +0 -0
  202. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/teaching/pack.py +0 -0
  203. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess/teaching/sheet.py +0 -0
  204. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess.egg-info/dependency_links.txt +0 -0
  205. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess.egg-info/entry_points.txt +0 -0
  206. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess.egg-info/requires.txt +0 -0
  207. {openprocess-0.11.0 → openprocess-0.12.0}/openprocess.egg-info/top_level.txt +0 -0
  208. {openprocess-0.11.0 → openprocess-0.12.0}/pyproject.toml +0 -0
  209. {openprocess-0.11.0 → openprocess-0.12.0}/setup.cfg +0 -0
  210. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_arc_editing.py +0 -0
  211. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_citation.py +0 -0
  212. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_colorsets.py +0 -0
  213. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_columns.py +0 -0
  214. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_cpn_course_model.py +0 -0
  215. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_definitions.py +0 -0
  216. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_exercises_gui.py +0 -0
  217. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_hierarchy.py +0 -0
  218. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_history.py +0 -0
  219. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_io.py +0 -0
  220. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_learn.py +0 -0
  221. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_learn_gui.py +0 -0
  222. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_log_editing.py +0 -0
  223. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_ml.py +0 -0
  224. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_multiset.py +0 -0
  225. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_ocel.py +0 -0
  226. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_packages.py +0 -0
  227. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_regions.py +0 -0
  228. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_rename.py +0 -0
  229. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_report.py +0 -0
  230. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_simulation.py +0 -0
  231. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_snapshots.py +0 -0
  232. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_state_space.py +0 -0
  233. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_teaching.py +0 -0
  234. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_tidy.py +0 -0
  235. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_updates.py +0 -0
  236. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_wf_structure.py +0 -0
  237. {openprocess-0.11.0 → openprocess-0.12.0}/tests/test_workspace.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openprocess
3
- Version: 0.11.0
3
+ Version: 0.12.0
4
4
  Summary: Process mining, Petri nets and workflows in one open app: every algorithm visible, every result reproducible, every step teachable
5
5
  Author: Sam Struthers
6
6
  License: MIT
@@ -281,8 +281,9 @@ which space you were in.
281
281
  state; it stays in the folder.
282
282
  - **Flat lists, the same in both spaces.** The folder's own files first,
283
283
  then each subfolder's under a caption naming it ("Week 5 / Part 1").
284
- Nothing to unfold except LOGS. An empty folder shows in both spaces until
285
- it has files.
284
+ Click a caption to fold its files away or bring them back; the app
285
+ remembers which are folded, per folder. An empty folder shows in both
286
+ spaces until it has files.
286
287
  - **Organise from the app**: right-click for **New Folder…**, **Rename…**,
287
288
  **Show in Finder** and **Move to Bin** (recoverable from the Bin; never a
288
289
  hard delete). **Drag files onto a subfolder** to move them on disk. An open
@@ -373,7 +374,9 @@ Nets drawn as in the lectures and the book:
373
374
  - **Places** are circles with black tokens inside, drawn as dots up to 5 and
374
375
  as a number above that.
375
376
  - **Transitions** are boxes with their name inside. A **silent (τ)**
376
- transition is a black bar.
377
+ transition is a black bar: the **τ** tool (toolbar or palette) adds one
378
+ with a click, and the Element tab's *Silent* box turns an ordinary
379
+ transition into τ and back.
377
380
  - **Arc weights** above 1 are written on the arc.
378
381
  - **Names** go inside the places and transitions, which grow to fit them.
379
382
  Tick **Names outside** in the toolbar to put them underneath instead; you
@@ -763,9 +766,14 @@ report = check_soundness(net)
763
766
  print(report.sound, report.findings)
764
767
  ```
765
768
 
766
- [**docs/how-it-works.md**](docs/how-it-works.md) covers the architecture, the
767
- CPN ML subset, binding search, timed nets, the state space, the file format
768
- and the project layout.
769
+ [**docs/architecture.md**](docs/architecture.md) maps the code: its four
770
+ layers, which package imports which (drawn from the code, and checked), how a
771
+ workflow runs, and every module. [**docs/how-it-works.md**](docs/how-it-works.md)
772
+ covers the CPN ML subset, binding search, timed nets, the state space and
773
+ the file format. [**docs/design.md**](docs/design.md) is the *why*:
774
+ the problem, the claim, the three ideas and the decisions that follow from
775
+ them. `tests/cross_check_inductive_miner.py` compares the built-in Inductive
776
+ Miner with PM4Py's on hundreds of logs and reports where they differ.
769
777
 
770
778
  ## Workflows
771
779
 
@@ -784,13 +792,12 @@ extra) and *Predict the next activity*. Or start empty.
784
792
  **+ Add box**, **Run ▶**, and **⋯** for Re-run, Record, Export experiment
785
793
  and Save. The sidebar beside it lists the folder's analyses and, folded
786
794
  under them, its logs.
787
- - **+ Add box** (or a double-click on the canvas) opens every box, by group:
788
- the six core groups side by side (Input, Filter, Discover, Check, Compare,
789
- Output) and one line for the rest (Science, Predict, Coloured nets, Sweep,
790
- your own boxes) with *Show all*. Under *Input*, **the folder's own files**:
791
- one click adds the box that reads the file, already set. Type to search
792
- them all: "alpha" finds the α-algorithm, "boarding" the log; Enter adds the
793
- first match.
795
+ - **+ Add box** (or a double-click on the canvas) opens every box, one
796
+ section per group (Input, Filter, Discover, Check, Measure, Compare, Output,
797
+ then Science, Predict, Coloured nets, Sweep and your own boxes), in a list that
798
+ scrolls. Under *Input*, **the folder's own files**: one click adds the box
799
+ that reads the file, already set. Type to search them all: "alpha" finds
800
+ the α-algorithm, "boarding" the log; Enter adds the first match.
794
801
  - **Files flow into the canvas.** Drop a log, an OCEL 2.0 object-centric log
795
802
  (`.jsonocel`), a PNML file, a .cpn or a ts.txt on the canvas and it becomes
796
803
  its input box at that spot; an input
@@ -848,8 +855,25 @@ extra) and *Predict the next activity*. Or start empty.
848
855
  metadata; a CSV gets a `.provenance.json` beside it.
849
856
  - **⌘Z and ⇧⌘Z** undo and redo on the canvas: boxes, connections, moves,
850
857
  settings, groups.
851
- - **The side panel** resizes by dragging the gap beside it; double-click the
852
- gap, or *View ▸ Reset Workflow Layout*, for its default width.
858
+ - **The side panel** resizes by dragging the gap beside it, never narrower
859
+ than what it shows; double-click the gap, or *View ▸ Reset Workflow Layout*,
860
+ for its default size. Its dock button (or *View ▸ Workflow Panel Under the
861
+ Canvas*) moves it under the canvas, where a wide table or the code has the
862
+ window's width; the app remembers where you put it.
863
+ - **Alignments** are drawn as ProM draws them: the log on the model (per
864
+ transition, ✓ synchronous moves and » model moves, its colour from green,
865
+ always matched, to purple, often skipped; *n* » log moves at the activity),
866
+ and every variant's alignment as a row of chevrons: green synchronous,
867
+ yellow log move, purple model move, black τ. On a large log, the box's
868
+ *variants* setting aligns the most frequent ones and says what share of the
869
+ cases they cover.
870
+ - **Measure** boxes answer the questions an audit asks, for every case: *Time
871
+ between* two activities (throughput), *Order rule* (does the payment block
872
+ go before the goods receipt?), *Segregation of duties* (who both recorded
873
+ and cleared an invoice?) and *Compliance by attribute* (per vendor: cases,
874
+ share that fits, value), with *Case attribute* to keep one category and
875
+ *Bubble chart* to draw it. [examples/bpi2019](examples/bpi2019/README.md)
876
+ uses them on the BPI Challenge 2019.
853
877
  - **Connections** (at the bottom of the sidebar, or *View ▸ Connections*)
854
878
  draws what flows in, OpenProcess and its boxes, what flows out, and the
855
879
  optional tools (pandas, numpy, scipy, matplotlib, …) with whether each is
@@ -891,7 +915,7 @@ save(wf, "orders.cpnflow", run)
891
915
  ```bash
892
916
  openprocess run orders.cpnflow --check # re-run; fails if any result differs from the record
893
917
  openprocess run orders.cpnflow --sweep noise=0..0.5 step 0.1
894
- openprocess boxes # the 47 boxes, with a folder's boxes/
918
+ openprocess boxes # every box (listed in docs/boxes.md), with a folder's boxes/
895
919
  openprocess datasets list # the BPI Challenge, Sepsis, ... logs by name
896
920
  ```
897
921
 
@@ -907,11 +931,22 @@ def last_two(log: EventLog, representation: str = "multiset") -> TransitionSyste
907
931
  ```
908
932
 
909
933
  `pip install -e ".[science]"` adds the pandas, NumPy, SciPy and matplotlib
910
- boxes (describe, bootstrap intervals, tests, plots). Large logs are read
911
- into columns rather than objects, so a million events fit in memory.
912
- [**docs/workflows.md**](docs/workflows.md) has the whole framework: writing
913
- a box, the types, the *How* tab, sweeps, the prediction pipeline, the
914
- record and running in CI.
934
+ boxes (describe, bootstrap intervals, tests, plots).
935
+
936
+ **Large logs.** A log above 20 MB is read into columns rather than objects,
937
+ and everything that looks at every event reads the columns: the BPI
938
+ Challenge 2019 log (251,734 cases, 1.6 million events, 728 MB) opens in
939
+ about 12 seconds and 250 MB, every tab of its log page in a few seconds, and
940
+ the whole [BPI 2019 analysis](examples/bpi2019/README.md), from reading the
941
+ file to the last chart, runs in about two minutes.
942
+
943
+ [**docs/workflows.md**](docs/workflows.md) is the tutorial: writing a box,
944
+ the types, the *How* tab, sweeps, the prediction pipeline, the record and
945
+ running in CI. [**docs/framework.md**](docs/framework.md) is the
946
+ specification: what a box, a workflow and a run are, what a fingerprint
947
+ covers and what a reproduced result promises. [**docs/architecture.md**](docs/architecture.md)
948
+ maps the code, drawn from its imports, and [**docs/boxes.md**](docs/boxes.md)
949
+ lists every box.
915
950
 
916
951
  **Building the apps.** `packaging/` turns OpenProcess into a standalone app with
917
952
  PyInstaller. In the environment:
@@ -934,11 +969,20 @@ workflow* on the Actions tab.
934
969
  [CHANGELOG.md](CHANGELOG.md) (`## 0.4.0`, then a few bullet points). Write
935
970
  it for the people using the app: it becomes the text of the release on
936
971
  GitHub, and the app shows it when it offers the update.
937
- 2. Set `__version__` in `openprocess/__init__.py` to the same version and merge
938
- both into `main`. It is the only place the version is written:
939
- `pyproject.toml`, the download names and the app's update check all read
940
- it from there.
941
- 3. Tag that commit with the same version and push the tag:
972
+ 2. Bring the documentation up to date with the code:
973
+ - run `python docs/make_docs.py`, which rewrites the code map in
974
+ [docs/architecture.md](docs/architecture.md) and the box catalogue in
975
+ [docs/boxes.md](docs/boxes.md) (the test suite, and so the release
976
+ build, fails while they are out of date, and when a package imports
977
+ across its layer);
978
+ - read [docs/framework.md](docs/framework.md) against what changed in
979
+ `openprocess/flow` (a test fails when a public name is not described
980
+ there, but only you can tell whether a description is still right).
981
+ 3. Set `__version__` in `openprocess/__init__.py` and `version` and
982
+ `date-released` in `CITATION.cff` to the same version, and merge them into
983
+ `main`. `pyproject.toml`, the download names and the app's update check
984
+ all read the version from `__version__`.
985
+ 4. Tag that commit with the same version and push the tag:
942
986
 
943
987
  ```bash
944
988
  git pull
@@ -949,8 +993,8 @@ workflow* on the Actions tab.
949
993
  About five minutes later the
950
994
  [Releases page](https://github.com/s4mstruthers/openprocess/releases) has the
951
995
  macOS (Apple silicon and Intel), Windows and Linux apps. The build refuses a
952
- tag that does not match `__version__`, or a version with no section in
953
- `CHANGELOG.md`.
996
+ tag that does not match `__version__`, a version with no section in
997
+ `CHANGELOG.md`, and code whose tests fail, stale documentation included.
954
998
 
955
999
  **Updates.** Each time it opens, the app checks the latest release on GitHub
956
1000
  (*Settings* turns that off). If there is a newer version, a slim bar at the
@@ -242,8 +242,9 @@ which space you were in.
242
242
  state; it stays in the folder.
243
243
  - **Flat lists, the same in both spaces.** The folder's own files first,
244
244
  then each subfolder's under a caption naming it ("Week 5 / Part 1").
245
- Nothing to unfold except LOGS. An empty folder shows in both spaces until
246
- it has files.
245
+ Click a caption to fold its files away or bring them back; the app
246
+ remembers which are folded, per folder. An empty folder shows in both
247
+ spaces until it has files.
247
248
  - **Organise from the app**: right-click for **New Folder…**, **Rename…**,
248
249
  **Show in Finder** and **Move to Bin** (recoverable from the Bin; never a
249
250
  hard delete). **Drag files onto a subfolder** to move them on disk. An open
@@ -334,7 +335,9 @@ Nets drawn as in the lectures and the book:
334
335
  - **Places** are circles with black tokens inside, drawn as dots up to 5 and
335
336
  as a number above that.
336
337
  - **Transitions** are boxes with their name inside. A **silent (τ)**
337
- transition is a black bar.
338
+ transition is a black bar: the **τ** tool (toolbar or palette) adds one
339
+ with a click, and the Element tab's *Silent* box turns an ordinary
340
+ transition into τ and back.
338
341
  - **Arc weights** above 1 are written on the arc.
339
342
  - **Names** go inside the places and transitions, which grow to fit them.
340
343
  Tick **Names outside** in the toolbar to put them underneath instead; you
@@ -724,9 +727,14 @@ report = check_soundness(net)
724
727
  print(report.sound, report.findings)
725
728
  ```
726
729
 
727
- [**docs/how-it-works.md**](docs/how-it-works.md) covers the architecture, the
728
- CPN ML subset, binding search, timed nets, the state space, the file format
729
- and the project layout.
730
+ [**docs/architecture.md**](docs/architecture.md) maps the code: its four
731
+ layers, which package imports which (drawn from the code, and checked), how a
732
+ workflow runs, and every module. [**docs/how-it-works.md**](docs/how-it-works.md)
733
+ covers the CPN ML subset, binding search, timed nets, the state space and
734
+ the file format. [**docs/design.md**](docs/design.md) is the *why*:
735
+ the problem, the claim, the three ideas and the decisions that follow from
736
+ them. `tests/cross_check_inductive_miner.py` compares the built-in Inductive
737
+ Miner with PM4Py's on hundreds of logs and reports where they differ.
730
738
 
731
739
  ## Workflows
732
740
 
@@ -745,13 +753,12 @@ extra) and *Predict the next activity*. Or start empty.
745
753
  **+ Add box**, **Run ▶**, and **⋯** for Re-run, Record, Export experiment
746
754
  and Save. The sidebar beside it lists the folder's analyses and, folded
747
755
  under them, its logs.
748
- - **+ Add box** (or a double-click on the canvas) opens every box, by group:
749
- the six core groups side by side (Input, Filter, Discover, Check, Compare,
750
- Output) and one line for the rest (Science, Predict, Coloured nets, Sweep,
751
- your own boxes) with *Show all*. Under *Input*, **the folder's own files**:
752
- one click adds the box that reads the file, already set. Type to search
753
- them all: "alpha" finds the α-algorithm, "boarding" the log; Enter adds the
754
- first match.
756
+ - **+ Add box** (or a double-click on the canvas) opens every box, one
757
+ section per group (Input, Filter, Discover, Check, Measure, Compare, Output,
758
+ then Science, Predict, Coloured nets, Sweep and your own boxes), in a list that
759
+ scrolls. Under *Input*, **the folder's own files**: one click adds the box
760
+ that reads the file, already set. Type to search them all: "alpha" finds
761
+ the α-algorithm, "boarding" the log; Enter adds the first match.
755
762
  - **Files flow into the canvas.** Drop a log, an OCEL 2.0 object-centric log
756
763
  (`.jsonocel`), a PNML file, a .cpn or a ts.txt on the canvas and it becomes
757
764
  its input box at that spot; an input
@@ -809,8 +816,25 @@ extra) and *Predict the next activity*. Or start empty.
809
816
  metadata; a CSV gets a `.provenance.json` beside it.
810
817
  - **⌘Z and ⇧⌘Z** undo and redo on the canvas: boxes, connections, moves,
811
818
  settings, groups.
812
- - **The side panel** resizes by dragging the gap beside it; double-click the
813
- gap, or *View ▸ Reset Workflow Layout*, for its default width.
819
+ - **The side panel** resizes by dragging the gap beside it, never narrower
820
+ than what it shows; double-click the gap, or *View ▸ Reset Workflow Layout*,
821
+ for its default size. Its dock button (or *View ▸ Workflow Panel Under the
822
+ Canvas*) moves it under the canvas, where a wide table or the code has the
823
+ window's width; the app remembers where you put it.
824
+ - **Alignments** are drawn as ProM draws them: the log on the model (per
825
+ transition, ✓ synchronous moves and » model moves, its colour from green,
826
+ always matched, to purple, often skipped; *n* » log moves at the activity),
827
+ and every variant's alignment as a row of chevrons: green synchronous,
828
+ yellow log move, purple model move, black τ. On a large log, the box's
829
+ *variants* setting aligns the most frequent ones and says what share of the
830
+ cases they cover.
831
+ - **Measure** boxes answer the questions an audit asks, for every case: *Time
832
+ between* two activities (throughput), *Order rule* (does the payment block
833
+ go before the goods receipt?), *Segregation of duties* (who both recorded
834
+ and cleared an invoice?) and *Compliance by attribute* (per vendor: cases,
835
+ share that fits, value), with *Case attribute* to keep one category and
836
+ *Bubble chart* to draw it. [examples/bpi2019](examples/bpi2019/README.md)
837
+ uses them on the BPI Challenge 2019.
814
838
  - **Connections** (at the bottom of the sidebar, or *View ▸ Connections*)
815
839
  draws what flows in, OpenProcess and its boxes, what flows out, and the
816
840
  optional tools (pandas, numpy, scipy, matplotlib, …) with whether each is
@@ -852,7 +876,7 @@ save(wf, "orders.cpnflow", run)
852
876
  ```bash
853
877
  openprocess run orders.cpnflow --check # re-run; fails if any result differs from the record
854
878
  openprocess run orders.cpnflow --sweep noise=0..0.5 step 0.1
855
- openprocess boxes # the 47 boxes, with a folder's boxes/
879
+ openprocess boxes # every box (listed in docs/boxes.md), with a folder's boxes/
856
880
  openprocess datasets list # the BPI Challenge, Sepsis, ... logs by name
857
881
  ```
858
882
 
@@ -868,11 +892,22 @@ def last_two(log: EventLog, representation: str = "multiset") -> TransitionSyste
868
892
  ```
869
893
 
870
894
  `pip install -e ".[science]"` adds the pandas, NumPy, SciPy and matplotlib
871
- boxes (describe, bootstrap intervals, tests, plots). Large logs are read
872
- into columns rather than objects, so a million events fit in memory.
873
- [**docs/workflows.md**](docs/workflows.md) has the whole framework: writing
874
- a box, the types, the *How* tab, sweeps, the prediction pipeline, the
875
- record and running in CI.
895
+ boxes (describe, bootstrap intervals, tests, plots).
896
+
897
+ **Large logs.** A log above 20 MB is read into columns rather than objects,
898
+ and everything that looks at every event reads the columns: the BPI
899
+ Challenge 2019 log (251,734 cases, 1.6 million events, 728 MB) opens in
900
+ about 12 seconds and 250 MB, every tab of its log page in a few seconds, and
901
+ the whole [BPI 2019 analysis](examples/bpi2019/README.md), from reading the
902
+ file to the last chart, runs in about two minutes.
903
+
904
+ [**docs/workflows.md**](docs/workflows.md) is the tutorial: writing a box,
905
+ the types, the *How* tab, sweeps, the prediction pipeline, the record and
906
+ running in CI. [**docs/framework.md**](docs/framework.md) is the
907
+ specification: what a box, a workflow and a run are, what a fingerprint
908
+ covers and what a reproduced result promises. [**docs/architecture.md**](docs/architecture.md)
909
+ maps the code, drawn from its imports, and [**docs/boxes.md**](docs/boxes.md)
910
+ lists every box.
876
911
 
877
912
  **Building the apps.** `packaging/` turns OpenProcess into a standalone app with
878
913
  PyInstaller. In the environment:
@@ -895,11 +930,20 @@ workflow* on the Actions tab.
895
930
  [CHANGELOG.md](CHANGELOG.md) (`## 0.4.0`, then a few bullet points). Write
896
931
  it for the people using the app: it becomes the text of the release on
897
932
  GitHub, and the app shows it when it offers the update.
898
- 2. Set `__version__` in `openprocess/__init__.py` to the same version and merge
899
- both into `main`. It is the only place the version is written:
900
- `pyproject.toml`, the download names and the app's update check all read
901
- it from there.
902
- 3. Tag that commit with the same version and push the tag:
933
+ 2. Bring the documentation up to date with the code:
934
+ - run `python docs/make_docs.py`, which rewrites the code map in
935
+ [docs/architecture.md](docs/architecture.md) and the box catalogue in
936
+ [docs/boxes.md](docs/boxes.md) (the test suite, and so the release
937
+ build, fails while they are out of date, and when a package imports
938
+ across its layer);
939
+ - read [docs/framework.md](docs/framework.md) against what changed in
940
+ `openprocess/flow` (a test fails when a public name is not described
941
+ there, but only you can tell whether a description is still right).
942
+ 3. Set `__version__` in `openprocess/__init__.py` and `version` and
943
+ `date-released` in `CITATION.cff` to the same version, and merge them into
944
+ `main`. `pyproject.toml`, the download names and the app's update check
945
+ all read the version from `__version__`.
946
+ 4. Tag that commit with the same version and push the tag:
903
947
 
904
948
  ```bash
905
949
  git pull
@@ -910,8 +954,8 @@ workflow* on the Actions tab.
910
954
  About five minutes later the
911
955
  [Releases page](https://github.com/s4mstruthers/openprocess/releases) has the
912
956
  macOS (Apple silicon and Intel), Windows and Linux apps. The build refuses a
913
- tag that does not match `__version__`, or a version with no section in
914
- `CHANGELOG.md`.
957
+ tag that does not match `__version__`, a version with no section in
958
+ `CHANGELOG.md`, and code whose tests fail, stale documentation included.
915
959
 
916
960
  **Updates.** Each time it opens, the app checks the latest release on GitHub
917
961
  (*Settings* turns that off). If there is a newer version, a slim bar at the
@@ -48,7 +48,7 @@ from .sim.simulator import Simulator
48
48
  #: The one place the version number is written (pyproject.toml, the update
49
49
  #: check and packaging/build.py read it from here). A release is tagged
50
50
  #: "v" + this, e.g. v0.2.0.
51
- __version__ = "0.11.0"
51
+ __version__ = "0.12.0"
52
52
 
53
53
  __all__ = [
54
54
  "Arc", "BindingElement", "CPNet", "Marking", "Multiset", "Page", "Place",
@@ -20,7 +20,8 @@ Commands
20
20
  ``exercises`` ``check``, ``marks``, ``import``, ``computes``: exercise packs (OpenProcess Learn)
21
21
  ``run`` run a ``.cpnflow`` workflow headless; ``--check`` re-runs and
22
22
  compares with the record, ``--sweep`` varies a setting,
23
- ``--lock`` writes the requirements the record names
23
+ ``--lock`` writes the requirements the record names,
24
+ ``--export`` and ``--report`` write a zip and an HTML page
24
25
  ``boxes`` list the boxes (OpenProcess's own, a folder's ``boxes/``, packages)
25
26
  ``datasets`` the public logs known by name: ``list``, ``fetch``, ``where``
26
27
  """
@@ -249,6 +250,8 @@ def command_mine_conform(arguments: argparse.Namespace) -> int:
249
250
  print(f"Generalisation {generalisation(replay):.4f}")
250
251
  print(f"Simplicity {simplicity(net):.4f}")
251
252
  print(f"Fitting cases {alignments.fitting_traces} / {alignments.trace_count}")
253
+ for note in alignments.notes:
254
+ print(f"Note: {note}")
252
255
  return 0
253
256
 
254
257
 
@@ -466,7 +469,7 @@ def command_run(arguments: argparse.Namespace) -> int:
466
469
  print(f"openprocess: {line}", file=sys.stderr)
467
470
  if problems:
468
471
  return 2
469
- run = Runner(library, folder=folder).run(workflow) # file settings are relative to the folder
472
+ run = Runner(library, folder=folder, strict=arguments.strict).run(workflow) # file settings: relative to the folder
470
473
  for node in workflow.order():
471
474
  result = run.result(node)
472
475
  value = run.value(node)
@@ -488,13 +491,21 @@ def command_run(arguments: argparse.Namespace) -> int:
488
491
  if arguments.export:
489
492
  from .flow.record import export_experiment
490
493
  print(f"Wrote {export_experiment(workflow, run, arguments.export, folder, library)}")
494
+ if arguments.report:
495
+ from .flow.report import write_report
496
+ print(f"Wrote {write_report(arguments.report, workflow=workflow, run=run, folder=folder)}")
491
497
  if arguments.check:
492
- report = check(workflow, record, run)
498
+ if not record.results:
499
+ # Nothing to compare is not a pass: a CI job would claim a reproduction that never happened.
500
+ print(f"openprocess: {path.name} has no recorded results to check against; "
501
+ f"record them first with: openprocess run {path.name} --save", file=sys.stderr)
502
+ return 1
503
+ report = check(workflow, record, run, complete=True)
493
504
  if report:
494
505
  for line in report:
495
506
  print(f"DIFFERS: {line}")
496
507
  return 1
497
- print("Every recorded result was reproduced.")
508
+ print(f"Every recorded result was reproduced ({len(record.results)} boxes).")
498
509
  elif arguments.save:
499
510
  save(workflow, path, run, folder)
500
511
  print(f"Recorded the results in {path.name}")
@@ -502,7 +513,11 @@ def command_run(arguments: argparse.Namespace) -> int:
502
513
 
503
514
 
504
515
  def _brief(value) -> str:
516
+ import dataclasses
517
+ from .flow.figures import tile_parts
505
518
  from .flow.types import Figure, Scores, Table
519
+ if dataclasses.is_dataclass(value) and not isinstance(value, type) and tile_parts(value):
520
+ return " · ".join(tile_parts(value))
506
521
  if isinstance(value, Scores):
507
522
  return ", ".join(f"{k} {v:.3f}" if isinstance(v, float) else f"{k} {v}" for k, v in value.metrics.items())
508
523
  if isinstance(value, Table):
@@ -648,6 +663,10 @@ def build_parser() -> argparse.ArgumentParser:
648
663
  run.add_argument("-o", "--output", metavar="FOLDER", help="write every result as a file")
649
664
  run.add_argument("--export", metavar="FILE.zip",
650
665
  help="zip the workflow, its inputs, boxes and results, with a README")
666
+ run.add_argument("--strict", action="store_true",
667
+ help="also fail a box that changes its inputs in place (slower; for tests and CI)")
668
+ run.add_argument("--report", metavar="FILE.html",
669
+ help="write the analysis as one HTML page: results, how, code, inputs, citations")
651
670
  run.add_argument("-v", "--verbose", action="store_true", help="also print the boxes' notes")
652
671
  run.set_defaults(handler=command_run)
653
672
 
@@ -11,18 +11,18 @@ Writing a box is writing a function with type hints, nothing more::
11
11
 
12
12
  What the decorator reads, and nothing else:
13
13
 
14
- ================================================ ===============================================
15
- parameters whose type is a OpenProcess type input connection points, named after the parameter
16
- ``list[Type]`` an input that takes any number of connections
17
- ``Type | None`` (or a default of ``None``) an optional input
18
- the return type the output; a dataclass of typed fields gives several
14
+ ==================================================== ========================================================
15
+ parameters whose type is an OpenProcess type input connection points, named after the parameter
16
+ ``list[Type]`` an input that takes any number of connections
17
+ ``Type | None`` (or a default of ``None``) an optional input
18
+ the return type the output; a dataclass of typed fields gives several
19
19
  ``int``, ``float``, ``bool``, ``str`` with a default a setting, with the default as its value
20
- ``Literal["a", "b"]`` a setting that is a choice
21
- ``Column`` a setting that is a column of a connected table
22
- ``Path`` a setting that is a file in the folder
23
- the docstring the help text
24
- ``inspect.getsource`` the Code tab
25
- ================================================ ===============================================
20
+ ``Literal["a", "b"]`` a setting that is a choice
21
+ ``Column`` a setting that is a column of a connected table
22
+ ``Path`` a setting that is a file in the folder
23
+ the docstring the help text; a ``name: text`` line is a setting's help
24
+ ``inspect.getsource`` the Code tab
25
+ ==================================================== ========================================================
26
26
 
27
27
  A box has no base class and no registration call: the decorator returns a
28
28
  :class:`Box`, which is still the function (call it from a script or a
@@ -39,6 +39,7 @@ import hashlib
39
39
  import inspect
40
40
  import os
41
41
  import importlib.util
42
+ import re
42
43
  import types
43
44
  import typing
44
45
  from dataclasses import dataclass, field
@@ -81,10 +82,11 @@ class Port:
81
82
  return "Anything" if self.type is None else getattr(self.type, "__name__", str(self.type))
82
83
 
83
84
  def accepts(self, other: "Port") -> bool:
84
- """Can an output ``other`` be connected to this input?"""
85
+ """Can an output ``other`` be connected to this input? The same type
86
+ or a subclass of it; a ``typing.Any`` point only to another one."""
85
87
  if self.type is None or other.type is None:
86
- return self.type is other.type and other.type is None or self.type is other.type
87
- return issubclass(other.type, self.type) or self.type is other.type
88
+ return self.type is other.type
89
+ return issubclass(other.type, self.type)
88
90
 
89
91
 
90
92
  def clean_path(value: Any) -> str:
@@ -113,8 +115,22 @@ class Setting:
113
115
  help: str = ""
114
116
 
115
117
  def coerce(self, value: Any) -> Any:
116
- """A value as the GUI or a file gives it, in the setting's type."""
118
+ """A value as the GUI or a file gives it, in the setting's type.
119
+ Raises ``ValueError`` for a value that is not one, rather than
120
+ rounding it: ``2.5`` is not an int setting's value, ``"2"`` and
121
+ ``2.0`` are."""
122
+ if value is None and self.default is None and self.kind != "bool":
123
+ return None # "no value" stays no value (str | None = None)
117
124
  if self.kind == "int":
125
+ if isinstance(value, (bool, int)):
126
+ return int(value)
127
+ if isinstance(value, str):
128
+ try:
129
+ return int(value.strip())
130
+ except ValueError:
131
+ value = float(value)
132
+ if isinstance(value, float) and not value.is_integer():
133
+ raise ValueError(f"{self.name!r} must be a whole number, not {value!r}")
118
134
  return int(value)
119
135
  if self.kind == "float":
120
136
  return float(value)
@@ -168,6 +184,20 @@ class BoxSpec:
168
184
  def defaults(self) -> dict[str, Any]:
169
185
  return {s.name: s.default for s in self.settings}
170
186
 
187
+ @property
188
+ def description(self) -> str:
189
+ """The docstring's prose: :attr:`help` without the ``name: text``
190
+ lines of its settings (they are shown next to each setting) and
191
+ without an ``Args:`` heading left with nothing under it."""
192
+ names = {s.name for s in self.settings} | {p.name for p in self.inputs}
193
+ lines = (self.help or "").splitlines()
194
+ dropped = {number for key, _text, first, last in _hint_blocks(self.help) if key in names
195
+ for number in range(first, last + 1)}
196
+ text = "\n".join(line for number, line in enumerate(lines) if number not in dropped)
197
+ for heading in ("Args:", "Arguments:", "Parameters:"):
198
+ text = re.sub(rf"(?m)^\s*{heading}\s*$\n?(?=\s*(\n|$))", "", text)
199
+ return text.strip()
200
+
171
201
  @property
172
202
  def fingerprint(self) -> str:
173
203
  """Changes when the code changes: part of every cache key."""
@@ -280,7 +310,7 @@ def _setting_from(name: str, annotation, default, has_default: bool, doc_hints:
280
310
  kind = "str"
281
311
  else:
282
312
  raise BoxError(
283
- f"parameter {name!r} has type {_type_text(annotation)}, which is neither a OpenProcess "
313
+ f"parameter {name!r} has type {_type_text(annotation)}, which is neither an OpenProcess "
284
314
  f"type (an input) nor int, float, bool, str, Literal[...], Column or Path (a setting)")
285
315
  if not has_default:
286
316
  raise BoxError(f"setting {name!r} needs a default value (e.g. {name}: {kind} = ...)")
@@ -298,39 +328,65 @@ def _outputs_from(annotation) -> tuple[list[Port], type | None]:
298
328
  raise BoxError("the function needs a return type hint (-> PetriNet), or -> None")
299
329
  if annotation is None or annotation is type(None):
300
330
  return [], None
301
- annotation, _ = _unwrap_optional(annotation)
331
+ annotation, optional = _unwrap_optional(annotation)
302
332
  if annotation is Any:
303
333
  return [Port("out", None)], None
304
334
  if isinstance(annotation, type) and is_known(annotation):
305
- return [Port("out", annotation)], None
335
+ return [Port("out", annotation, optional=optional)], None
306
336
  if isinstance(annotation, type) and dataclasses.is_dataclass(annotation):
307
337
  ports = []
308
338
  hints = get_type_hints(annotation)
309
339
  for f in dataclasses.fields(annotation):
310
- inner, _ = _unwrap_optional(hints.get(f.name, f.type))
340
+ inner, optional = _unwrap_optional(hints.get(f.name, f.type))
311
341
  if not (isinstance(inner, type) and is_known(inner)) and inner is not Any:
312
342
  raise BoxError(f"result field {f.name!r} has type {_type_text(inner)}, "
313
- f"which is not a OpenProcess type")
314
- ports.append(Port(f.name, None if inner is Any else inner, label=f.name.replace("_", " ")))
343
+ f"which is not an OpenProcess type")
344
+ ports.append(Port(f.name, None if inner is Any else inner, optional=optional,
345
+ label=f.name.replace("_", " ")))
315
346
  if not ports:
316
347
  raise BoxError(f"result class {annotation.__name__} has no fields")
317
348
  return ports, annotation
318
- raise BoxError(f"the return type {_type_text(annotation)} is not a OpenProcess type "
349
+ raise BoxError(f"the return type {_type_text(annotation)} is not an OpenProcess type "
319
350
  f"(nor a dataclass of them)")
320
351
 
321
352
 
322
353
  def _doc_hints(doc: str) -> dict[str, str]:
323
- """``name: text`` lines of a docstring, as help for settings."""
354
+ """``name: text`` lines of a docstring, as help for settings. The
355
+ Google style ``name (float): text`` is read the same way, so a function
356
+ documented for Sphinx needs no second docstring to be a box."""
324
357
  hints: dict[str, str] = {}
325
- for line in (doc or "").splitlines():
326
- stripped = line.strip()
327
- if ":" in stripped and not stripped.startswith(":"):
328
- key, _, text = stripped.partition(":")
329
- if key.isidentifier() and text.strip():
330
- hints[key] = text.strip()
358
+ for key, text, _first, _last in _hint_blocks(doc):
359
+ hints.setdefault(key, text)
331
360
  return hints
332
361
 
333
362
 
363
+ def _hint_blocks(doc: str):
364
+ """``(name, help, first line, last line)`` for each ``name: text`` block of a
365
+ docstring: the line and the lines under it until a blank line or the next
366
+ ``name:``, so a help that goes on over several lines stays one help."""
367
+ lines = (doc or "").splitlines()
368
+ index = 0
369
+ while index < len(lines):
370
+ stripped = lines[index].strip()
371
+ key, _, text = stripped.partition(":")
372
+ key = key.split("(", 1)[0].strip() # "noise (float)" -> "noise"
373
+ if ":" in stripped and not stripped.startswith(":") and key.isidentifier() and text.strip():
374
+ parts, end = [text.strip()], index + 1
375
+ while end < len(lines) and lines[end].strip() and not _starts_hint(lines[end]):
376
+ parts.append(lines[end].strip())
377
+ end += 1
378
+ yield key, " ".join(parts), index, end - 1
379
+ index = end
380
+ else:
381
+ index += 1
382
+
383
+
384
+ def _starts_hint(line: str) -> bool:
385
+ stripped = line.strip()
386
+ key, _, text = stripped.partition(":")
387
+ return ":" in stripped and key.split("(", 1)[0].strip().isidentifier() and bool(text.strip())
388
+
389
+
334
390
  def _plain_name(function_name: str) -> str:
335
391
  words = function_name.replace("_", " ").strip()
336
392
  return words[:1].upper() + words[1:]