openprocess 0.10.5__tar.gz → 0.11.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.
- {openprocess-0.10.5/openprocess.egg-info → openprocess-0.11.0}/PKG-INFO +45 -10
- {openprocess-0.10.5 → openprocess-0.11.0}/README.md +44 -9
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/__init__.py +1 -1
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/citation.py +37 -10
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/flow/__init__.py +2 -1
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/flow/box.py +6 -3
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/flow/boxes/science.py +26 -15
- openprocess-0.11.0/openprocess/flow/history.py +113 -0
- openprocess-0.11.0/openprocess/flow/provenance.py +110 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/flow/record.py +67 -3
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/flow/reproducibility.py +9 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/flow/runner.py +38 -1
- openprocess-0.11.0/openprocess/flow/script.py +87 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/flow/types.py +22 -1
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/flow/workflow.py +18 -1
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/flow/page.py +88 -6
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/flow/viewers.py +122 -7
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/learn/answer_boxes.py +3 -5
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/app.py +124 -15
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/connections.py +49 -13
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/definition_view.py +9 -2
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/graph_view.py +9 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/markdown_view.py +3 -1
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/style.py +5 -9
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/widgets.py +102 -10
- openprocess-0.11.0/openprocess/packages.py +139 -0
- {openprocess-0.10.5 → openprocess-0.11.0/openprocess.egg-info}/PKG-INFO +45 -10
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess.egg-info/SOURCES.txt +6 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/tests/test_citation.py +27 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/tests/test_exercises_gui.py +6 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/tests/test_flow.py +170 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/tests/test_flow_gui.py +185 -4
- openprocess-0.11.0/tests/test_history.py +57 -0
- openprocess-0.11.0/tests/test_packages.py +69 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/tests/test_studio_gui.py +100 -1
- {openprocess-0.10.5 → openprocess-0.11.0}/LICENSE +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/cpnpy/__init__.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/analysis/__init__.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/analysis/state_space.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/analysis/state_space_process.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/cli.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/exercises/1 Petri nets/Exercise 1.1 Order handling/answer.pnml +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/exercises/1 Petri nets/Exercise 1.1 Order handling/question.md +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/exercises/2 Soundness/Exercise 2.1 Spot the flaw/answer.pnml +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/exercises/2 Soundness/Exercise 2.1 Spot the flaw/net.pnml +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/exercises/2 Soundness/Exercise 2.1 Spot the flaw/question.md +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/exercises/3 Discovery/Exercise 3.1 The alpha-algorithm/log.txt +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/exercises/3 Discovery/Exercise 3.1 The alpha-algorithm/question.md +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/exercises/4 Regions/Exercise 4.1 Regions of a transition system/question.md +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/exercises/4 Regions/Exercise 4.1 Regions of a transition system/ts.txt +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/exercises/5 Markings/Exercise 5.1 Markings and matrices/net.pnml +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/exercises/5 Markings/Exercise 5.1 Markings and matrices/question.md +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/exercises/6 Inductive Miner/Exercise 6.1 Cuts and trees/log.txt +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/exercises/6 Inductive Miner/Exercise 6.1 Cuts and trees/question.md +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/log.txt +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/m1.pnml +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/m2.pnml +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/m3.pnml +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/net.pnml +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/question.md +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/exercises/pack.md +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/flow/boxes/__init__.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/flow/boxes/check.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/flow/boxes/compare.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/flow/boxes/cpn.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/flow/boxes/discover.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/flow/boxes/filter.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/flow/boxes/input.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/flow/boxes/output.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/flow/boxes/predict.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/flow/boxes/sweeps.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/flow/convert.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/flow/datasets.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/flow/explain.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/flow/figures.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/flow/library.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/flow/report.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/flow/snapshots.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/flow/sweep.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/__init__.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/app.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/arc_editing.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/canvas.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/flow/__init__.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/flow/canvas.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/flow/picker.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/flow/runs.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/flow/stepper.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/flow/summary.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/flow/templates.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/items.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/learn/concealment.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/learn/mode.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/panning.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/resources/openprocess-icon.png +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/__init__.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/__main__.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/charts.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/cite.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/command_palette.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/compare_page.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/cpn_page.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/derivation_view.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/documents.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/dotted_chart.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/file_dialogs.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/filter_dialog.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/graph_builders.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/instances.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/log_editor.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/log_page.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/mathtext.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/ml_highlighter.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/model_page.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/motion.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/net_comparison.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/notes_overlay.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/petri_page.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/provenance.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/regions_view.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/sidebar.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/spaces.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/tool_icons.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/updates.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/workers.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/studio/workspace.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/theme.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/gui/tidy.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/io/__init__.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/io/cpn_reader.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/io/cpn_writer.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/learn/__init__.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/learn/answers.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/learn/checks.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/learn/computed.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/learn/context.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/learn/exam.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/learn/exercise-packs.md +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/learn/importer.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/learn/notation.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/learn/pack.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/learn/sheet.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/__init__.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/analysis.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/columns.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/compare_nets.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/conformance/__init__.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/conformance/alignments.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/conformance/quality.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/conformance/token_replay.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/csv_import.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/definitions.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/dfg.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/discovery/__init__.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/discovery/alpha.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/discovery/heuristics.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/discovery/inductive.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/discovery/state_regions.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/filtering.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/footprint.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/invariants.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/layout.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/log.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/ocel.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/petrinet.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/playout.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/pm4py_bridge.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/pnml.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/processtree.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/regions.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/stats.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/structure.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/transition_system.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/mining/xes.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/ml/__init__.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/ml/ast_nodes.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/ml/builtins.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/ml/colorsets.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/ml/errors.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/ml/evaluator.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/ml/lexer.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/ml/multiset.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/ml/parser.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/ml/values.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/model/__init__.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/model/declarations.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/model/examples.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/model/net.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/model/plain.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/references.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/sim/__init__.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/sim/binding.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/sim/export.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/sim/simulator.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/teaching/__init__.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/teaching/answers.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/teaching/checks.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/teaching/pack.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess/teaching/sheet.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess.egg-info/dependency_links.txt +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess.egg-info/entry_points.txt +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess.egg-info/requires.txt +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/openprocess.egg-info/top_level.txt +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/pyproject.toml +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/setup.cfg +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/tests/test_arc_editing.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/tests/test_colorsets.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/tests/test_columns.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/tests/test_cpn_course_model.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/tests/test_definitions.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/tests/test_hierarchy.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/tests/test_io.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/tests/test_learn.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/tests/test_learn_gui.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/tests/test_log_editing.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/tests/test_mining.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/tests/test_ml.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/tests/test_multiset.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/tests/test_ocel.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/tests/test_regions.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/tests/test_rename.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/tests/test_report.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/tests/test_simulation.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/tests/test_snapshots.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/tests/test_state_space.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/tests/test_teaching.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/tests/test_tidy.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/tests/test_updates.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/tests/test_wf_structure.py +0 -0
- {openprocess-0.10.5 → openprocess-0.11.0}/tests/test_workspace.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openprocess
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.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
|
|
@@ -528,17 +528,19 @@ links to the definitions it builds on; **Help ▸ Definitions** lists them all.
|
|
|
528
528
|
for bounded nets; for unbounded ones, up to a trace length (and it says
|
|
529
529
|
so).
|
|
530
530
|
|
|
531
|
-
| Process map |
|
|
531
|
+
| Process map | Discover in Mine →: the miner is a box, its result beside the canvas |
|
|
532
532
|
|---|---|
|
|
533
533
|
|  |  |
|
|
534
534
|
|
|
535
|
-
| α-algorithm result |
|
|
535
|
+
| α-algorithm result | *How*: the eight steps, from the footprint to α(L) |
|
|
536
536
|
|---|---|
|
|
537
|
-
|  |  |  |
|
|
538
538
|
|
|
539
|
-
|
|
|
539
|
+
| Check fit: every score, and how it was computed | Comparing two logs |
|
|
540
540
|
|---|---|
|
|
541
|
-
|  |  |
|
|
542
|
+
|
|
543
|
+

|
|
542
544
|
|
|
543
545
|
The app follows the system's light or dark appearance:
|
|
544
546
|
|
|
@@ -555,6 +557,8 @@ sidebar, **Learn ▸ Open Exercise Pack…**, or **Learn ▸ Open Demo Exercises
|
|
|
555
557
|
exercises): the window switches to a view made for working through a pack
|
|
556
558
|
without distractions, and *Exit* brings your folder back exactly as it was.
|
|
557
559
|
|
|
560
|
+

|
|
561
|
+
|
|
558
562
|
- **The worksheet** on the left is the question top to bottom, with an
|
|
559
563
|
answer box wherever one is needed: yes/no, multiple choice, a set
|
|
560
564
|
(`{a, b}`, sets of sets, or pairs like `({a}, {b,d})`), a number, a short
|
|
@@ -742,7 +746,10 @@ generated from `openprocess/mining/definitions.py`; after editing a definition,
|
|
|
742
746
|
Likewise [`docs/references.md`](docs/references.md) — every source OpenProcess's
|
|
743
747
|
notation and algorithms follow, also in the app under **Help ▸ References** —
|
|
744
748
|
is generated from `openprocess/references.py` with
|
|
745
|
-
`python -m openprocess.references > docs/references.md`.
|
|
749
|
+
`python -m openprocess.references > docs/references.md`. The screenshots in
|
|
750
|
+
this README come from the app itself: `python docs/make_screenshots.py`
|
|
751
|
+
renders every page offscreen into `docs/screenshots/`, so run it after a
|
|
752
|
+
change to the app's looks.
|
|
746
753
|
|
|
747
754
|
The engines (`openprocess.mining`, `openprocess.ml`, `openprocess.sim`, `openprocess.analysis`)
|
|
748
755
|
have **no dependencies**, so they work in a notebook or a script:
|
|
@@ -769,6 +776,10 @@ Miner, a fitness check), *Compare discovery* (three miners side by side),
|
|
|
769
776
|
confidence* (bootstrap intervals, a test and a plot; needs the `science`
|
|
770
777
|
extra) and *Predict the next activity*. Or start empty.
|
|
771
778
|
|
|
779
|
+
| The canvas: *Compare discovery*, three miners side by side | Summary: every box's key figure, and the process map |
|
|
780
|
+
|---|---|
|
|
781
|
+
|  |  |
|
|
782
|
+
|
|
772
783
|
- **The page is the canvas.** Its header has **Canvas | Summary**, then
|
|
773
784
|
**+ Add box**, **Run ▶**, and **⋯** for Re-run, Record, Export experiment
|
|
774
785
|
and Save. The sidebar beside it lists the folder's analyses and, folded
|
|
@@ -825,15 +836,29 @@ extra) and *Predict the next activity*. Or start empty.
|
|
|
825
836
|
`boxes/`, so it is listed under *Yours* and can be used in any workflow.
|
|
826
837
|
- **Export experiment** (under ⋯) writes a zip with the workflow file and its record,
|
|
827
838
|
the inputs, your boxes, every result as a file (CSV, PNML, SVG, XES), a
|
|
828
|
-
`requirements.lock
|
|
829
|
-
|
|
839
|
+
`requirements.lock`, a `run.py` that runs the same analysis as a plain
|
|
840
|
+
script, and a README that says what was run: supplementary material for a
|
|
841
|
+
paper, which `openprocess run --check` can verify. Drop such a zip on
|
|
842
|
+
OpenProcess and it is unpacked into a folder of its own and run again.
|
|
843
|
+
- **Export as Python script** (under ⋯) writes the analysis as a `.py` file:
|
|
844
|
+
the same boxes, settings and connections, run with `python analysis.py`,
|
|
845
|
+
the results written beside it.
|
|
846
|
+
- **Every result knows where it came from**: a figure saved as SVG carries
|
|
847
|
+
the analysis, the box, its settings and the result's fingerprint in its
|
|
848
|
+
metadata; a CSV gets a `.provenance.json` beside it.
|
|
849
|
+
- **⌘Z and ⇧⌘Z** undo and redo on the canvas: boxes, connections, moves,
|
|
850
|
+
settings, groups.
|
|
830
851
|
- **The side panel** resizes by dragging the gap beside it; double-click the
|
|
831
852
|
gap, or *View ▸ Reset Workflow Layout*, for its default width.
|
|
832
853
|
- **Connections** (at the bottom of the sidebar, or *View ▸ Connections*)
|
|
833
854
|
draws what flows in, OpenProcess and its boxes, what flows out, and the
|
|
834
855
|
optional tools (pandas, numpy, scipy, matplotlib, …) with whether each is
|
|
835
856
|
installed, how to install it and the boxes it brings. All of it is read
|
|
836
|
-
from the box library.
|
|
857
|
+
from the box library. *Install…* works in the downloaded app too: it carries
|
|
858
|
+
pip and installs into a folder of your own (on a Mac,
|
|
859
|
+
`~/Library/Application Support/OpenProcess/packages`), and the boxes that
|
|
860
|
+
need the package come alive without a restart. PM4Py needs a Python
|
|
861
|
+
install (`pip install pm4py`), as parts of it are built from source.
|
|
837
862
|
- **Motion** is kept short: a space switch, the side panel and Canvas ↔
|
|
838
863
|
Summary settle in a fifth of a second, and a box pulses once when it
|
|
839
864
|
finishes. `OPENPROCESS_NO_MOTION=1` turns it off.
|
|
@@ -969,6 +994,16 @@ Plainly stated:
|
|
|
969
994
|
Next up: several instances of a subpage, CPN monitors, and signed apps that
|
|
970
995
|
open without a security prompt.
|
|
971
996
|
|
|
997
|
+
## Cite
|
|
998
|
+
|
|
999
|
+
If OpenProcess helped with a course, a thesis or a paper, cite it. *Help ▸
|
|
1000
|
+
Cite OpenProcess…* in the app gives BibTeX and a one-line citation, and
|
|
1001
|
+
each algorithm's Code tab has *Cite* for the paper it follows. GitHub's
|
|
1002
|
+
*Cite this repository* reads [CITATION.cff](CITATION.cff). Releases are
|
|
1003
|
+
archived on Zenodo, which gives each version a DOI; the all-versions DOI
|
|
1004
|
+
goes into `CITATION.cff` (`doi:`) once the first release is archived, and
|
|
1005
|
+
the app's citations pick it up from there.
|
|
1006
|
+
|
|
972
1007
|
## Licence
|
|
973
1008
|
|
|
974
1009
|
MIT. See [LICENSE](LICENSE).
|
|
@@ -489,17 +489,19 @@ links to the definitions it builds on; **Help ▸ Definitions** lists them all.
|
|
|
489
489
|
for bounded nets; for unbounded ones, up to a trace length (and it says
|
|
490
490
|
so).
|
|
491
491
|
|
|
492
|
-
| Process map |
|
|
492
|
+
| Process map | Discover in Mine →: the miner is a box, its result beside the canvas |
|
|
493
493
|
|---|---|
|
|
494
494
|
|  |  |
|
|
495
495
|
|
|
496
|
-
| α-algorithm result |
|
|
496
|
+
| α-algorithm result | *How*: the eight steps, from the footprint to α(L) |
|
|
497
497
|
|---|---|
|
|
498
|
-
|  |  |  |
|
|
499
499
|
|
|
500
|
-
|
|
|
500
|
+
| Check fit: every score, and how it was computed | Comparing two logs |
|
|
501
501
|
|---|---|
|
|
502
|
-
|  |  |
|
|
503
|
+
|
|
504
|
+

|
|
503
505
|
|
|
504
506
|
The app follows the system's light or dark appearance:
|
|
505
507
|
|
|
@@ -516,6 +518,8 @@ sidebar, **Learn ▸ Open Exercise Pack…**, or **Learn ▸ Open Demo Exercises
|
|
|
516
518
|
exercises): the window switches to a view made for working through a pack
|
|
517
519
|
without distractions, and *Exit* brings your folder back exactly as it was.
|
|
518
520
|
|
|
521
|
+

|
|
522
|
+
|
|
519
523
|
- **The worksheet** on the left is the question top to bottom, with an
|
|
520
524
|
answer box wherever one is needed: yes/no, multiple choice, a set
|
|
521
525
|
(`{a, b}`, sets of sets, or pairs like `({a}, {b,d})`), a number, a short
|
|
@@ -703,7 +707,10 @@ generated from `openprocess/mining/definitions.py`; after editing a definition,
|
|
|
703
707
|
Likewise [`docs/references.md`](docs/references.md) — every source OpenProcess's
|
|
704
708
|
notation and algorithms follow, also in the app under **Help ▸ References** —
|
|
705
709
|
is generated from `openprocess/references.py` with
|
|
706
|
-
`python -m openprocess.references > docs/references.md`.
|
|
710
|
+
`python -m openprocess.references > docs/references.md`. The screenshots in
|
|
711
|
+
this README come from the app itself: `python docs/make_screenshots.py`
|
|
712
|
+
renders every page offscreen into `docs/screenshots/`, so run it after a
|
|
713
|
+
change to the app's looks.
|
|
707
714
|
|
|
708
715
|
The engines (`openprocess.mining`, `openprocess.ml`, `openprocess.sim`, `openprocess.analysis`)
|
|
709
716
|
have **no dependencies**, so they work in a notebook or a script:
|
|
@@ -730,6 +737,10 @@ Miner, a fitness check), *Compare discovery* (three miners side by side),
|
|
|
730
737
|
confidence* (bootstrap intervals, a test and a plot; needs the `science`
|
|
731
738
|
extra) and *Predict the next activity*. Or start empty.
|
|
732
739
|
|
|
740
|
+
| The canvas: *Compare discovery*, three miners side by side | Summary: every box's key figure, and the process map |
|
|
741
|
+
|---|---|
|
|
742
|
+
|  |  |
|
|
743
|
+
|
|
733
744
|
- **The page is the canvas.** Its header has **Canvas | Summary**, then
|
|
734
745
|
**+ Add box**, **Run ▶**, and **⋯** for Re-run, Record, Export experiment
|
|
735
746
|
and Save. The sidebar beside it lists the folder's analyses and, folded
|
|
@@ -786,15 +797,29 @@ extra) and *Predict the next activity*. Or start empty.
|
|
|
786
797
|
`boxes/`, so it is listed under *Yours* and can be used in any workflow.
|
|
787
798
|
- **Export experiment** (under ⋯) writes a zip with the workflow file and its record,
|
|
788
799
|
the inputs, your boxes, every result as a file (CSV, PNML, SVG, XES), a
|
|
789
|
-
`requirements.lock
|
|
790
|
-
|
|
800
|
+
`requirements.lock`, a `run.py` that runs the same analysis as a plain
|
|
801
|
+
script, and a README that says what was run: supplementary material for a
|
|
802
|
+
paper, which `openprocess run --check` can verify. Drop such a zip on
|
|
803
|
+
OpenProcess and it is unpacked into a folder of its own and run again.
|
|
804
|
+
- **Export as Python script** (under ⋯) writes the analysis as a `.py` file:
|
|
805
|
+
the same boxes, settings and connections, run with `python analysis.py`,
|
|
806
|
+
the results written beside it.
|
|
807
|
+
- **Every result knows where it came from**: a figure saved as SVG carries
|
|
808
|
+
the analysis, the box, its settings and the result's fingerprint in its
|
|
809
|
+
metadata; a CSV gets a `.provenance.json` beside it.
|
|
810
|
+
- **⌘Z and ⇧⌘Z** undo and redo on the canvas: boxes, connections, moves,
|
|
811
|
+
settings, groups.
|
|
791
812
|
- **The side panel** resizes by dragging the gap beside it; double-click the
|
|
792
813
|
gap, or *View ▸ Reset Workflow Layout*, for its default width.
|
|
793
814
|
- **Connections** (at the bottom of the sidebar, or *View ▸ Connections*)
|
|
794
815
|
draws what flows in, OpenProcess and its boxes, what flows out, and the
|
|
795
816
|
optional tools (pandas, numpy, scipy, matplotlib, …) with whether each is
|
|
796
817
|
installed, how to install it and the boxes it brings. All of it is read
|
|
797
|
-
from the box library.
|
|
818
|
+
from the box library. *Install…* works in the downloaded app too: it carries
|
|
819
|
+
pip and installs into a folder of your own (on a Mac,
|
|
820
|
+
`~/Library/Application Support/OpenProcess/packages`), and the boxes that
|
|
821
|
+
need the package come alive without a restart. PM4Py needs a Python
|
|
822
|
+
install (`pip install pm4py`), as parts of it are built from source.
|
|
798
823
|
- **Motion** is kept short: a space switch, the side panel and Canvas ↔
|
|
799
824
|
Summary settle in a fifth of a second, and a box pulses once when it
|
|
800
825
|
finishes. `OPENPROCESS_NO_MOTION=1` turns it off.
|
|
@@ -930,6 +955,16 @@ Plainly stated:
|
|
|
930
955
|
Next up: several instances of a subpage, CPN monitors, and signed apps that
|
|
931
956
|
open without a security prompt.
|
|
932
957
|
|
|
958
|
+
## Cite
|
|
959
|
+
|
|
960
|
+
If OpenProcess helped with a course, a thesis or a paper, cite it. *Help ▸
|
|
961
|
+
Cite OpenProcess…* in the app gives BibTeX and a one-line citation, and
|
|
962
|
+
each algorithm's Code tab has *Cite* for the paper it follows. GitHub's
|
|
963
|
+
*Cite this repository* reads [CITATION.cff](CITATION.cff). Releases are
|
|
964
|
+
archived on Zenodo, which gives each version a DOI; the all-versions DOI
|
|
965
|
+
goes into `CITATION.cff` (`doi:`) once the first release is archived, and
|
|
966
|
+
the app's citations pick it up from there.
|
|
967
|
+
|
|
933
968
|
## Licence
|
|
934
969
|
|
|
935
970
|
MIT. See [LICENSE](LICENSE).
|
|
@@ -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.
|
|
51
|
+
__version__ = "0.11.0"
|
|
52
52
|
|
|
53
53
|
__all__ = [
|
|
54
54
|
"Arc", "BindingElement", "CPNet", "Marking", "Multiset", "Page", "Place",
|
|
@@ -14,6 +14,7 @@ from __future__ import annotations
|
|
|
14
14
|
import datetime as _dt
|
|
15
15
|
import re
|
|
16
16
|
from dataclasses import dataclass, field
|
|
17
|
+
from pathlib import Path
|
|
17
18
|
|
|
18
19
|
from . import references as _references
|
|
19
20
|
|
|
@@ -27,6 +28,23 @@ def app_version() -> str:
|
|
|
27
28
|
return __version__
|
|
28
29
|
|
|
29
30
|
|
|
31
|
+
def concept_doi() -> str:
|
|
32
|
+
"""The DOI Zenodo gives the software (all versions), from ``CITATION.cff``
|
|
33
|
+
at the top of the repository or installed beside the package; "" until
|
|
34
|
+
the first release is archived."""
|
|
35
|
+
import re
|
|
36
|
+
for candidate in (Path(__file__).resolve().parents[1] / "CITATION.cff",
|
|
37
|
+
Path(__file__).resolve().parent / "CITATION.cff"):
|
|
38
|
+
try:
|
|
39
|
+
text = candidate.read_text(encoding="utf-8")
|
|
40
|
+
except OSError:
|
|
41
|
+
continue
|
|
42
|
+
found = re.search(r"^doi:\s*(\S+)\s*$", text, re.M)
|
|
43
|
+
if found:
|
|
44
|
+
return found.group(1).strip("'\"")
|
|
45
|
+
return ""
|
|
46
|
+
|
|
47
|
+
|
|
30
48
|
# ---------------------------------------------------------------------------
|
|
31
49
|
# One entry
|
|
32
50
|
# ---------------------------------------------------------------------------
|
|
@@ -137,45 +155,54 @@ def entries_for_module(module: str) -> list[Entry]:
|
|
|
137
155
|
# ---------------------------------------------------------------------------
|
|
138
156
|
# The app itself
|
|
139
157
|
# ---------------------------------------------------------------------------
|
|
140
|
-
def app_entry(version: str | None = None, year: int | None = None) -> Entry:
|
|
158
|
+
def app_entry(version: str | None = None, year: int | None = None, doi: str | None = None) -> Entry:
|
|
141
159
|
version = version or app_version()
|
|
142
160
|
year = year or _dt.date.today().year
|
|
161
|
+
doi = concept_doi() if doi is None else doi
|
|
143
162
|
return Entry("software", "openprocess", {
|
|
144
163
|
"author": " and ".join(f"{last}, {first}" for last, first in APP_AUTHORS),
|
|
145
164
|
"title": APP_TITLE,
|
|
146
165
|
"version": version,
|
|
147
166
|
"year": str(year),
|
|
167
|
+
"doi": doi,
|
|
148
168
|
"url": APP_URL,
|
|
149
169
|
"note": "Every algorithm shows its code and the paper it follows; every analysis keeps a record "
|
|
150
170
|
"that runs again",
|
|
151
171
|
})
|
|
152
172
|
|
|
153
173
|
|
|
154
|
-
def app_bibtex(version: str | None = None, year: int | None = None) -> str:
|
|
155
|
-
return app_entry(version, year).bibtex()
|
|
174
|
+
def app_bibtex(version: str | None = None, year: int | None = None, doi: str | None = None) -> str:
|
|
175
|
+
return app_entry(version, year, doi).bibtex()
|
|
156
176
|
|
|
157
177
|
|
|
158
|
-
def app_text(version: str | None = None, year: int | None = None) -> str:
|
|
178
|
+
def app_text(version: str | None = None, year: int | None = None, doi: str | None = None) -> str:
|
|
159
179
|
"""The citation in one line, as a reference list would have it."""
|
|
160
180
|
version = version or app_version()
|
|
161
181
|
year = year or _dt.date.today().year
|
|
182
|
+
doi = concept_doi() if doi is None else doi
|
|
162
183
|
authors = ", ".join(f"{first} {last}" for last, first in APP_AUTHORS)
|
|
163
|
-
|
|
184
|
+
where = f"https://doi.org/{doi}" if doi else APP_URL
|
|
185
|
+
return f"{authors}. {APP_TITLE} (version {version}) [software], {year}. {where}"
|
|
164
186
|
|
|
165
187
|
|
|
166
|
-
def cff_text(version: str | None = None, released: _dt.date | None = None) -> str:
|
|
167
|
-
"""The contents of ``CITATION.cff`` (Citation File Format 1.2.0) for
|
|
188
|
+
def cff_text(version: str | None = None, released: _dt.date | None = None, doi: str = "") -> str:
|
|
189
|
+
"""The contents of ``CITATION.cff`` (Citation File Format 1.2.0) for
|
|
190
|
+
``version``; ``doi`` is Zenodo's concept DOI once the first release is archived."""
|
|
168
191
|
version = version or app_version()
|
|
169
192
|
released = released or _dt.date.today()
|
|
170
193
|
authors = "\n".join(f" - family-names: {last}\n given-names: {first}" for last, first in APP_AUTHORS)
|
|
194
|
+
def quoted(text: str) -> str: # a YAML string: a colon in the title would otherwise break it
|
|
195
|
+
return '"' + text.replace('\\', '\\\\').replace('"', '\\"') + '"'
|
|
196
|
+
|
|
171
197
|
return (
|
|
172
198
|
"cff-version: 1.2.0\n"
|
|
173
|
-
"message: If you use OpenProcess in teaching or research, please cite it as below
|
|
199
|
+
f"message: {quoted('If you use OpenProcess in teaching or research, please cite it as below.')}\n"
|
|
174
200
|
"type: software\n"
|
|
175
|
-
f"title: {APP_TITLE}\n"
|
|
201
|
+
f"title: {quoted(APP_TITLE)}\n"
|
|
176
202
|
f"version: {version}\n"
|
|
177
203
|
f"date-released: {released.isoformat()}\n"
|
|
178
|
-
f"
|
|
204
|
+
+ (f"doi: {doi}\n" if doi else "")
|
|
205
|
+
+ f"repository-code: {APP_URL}\n"
|
|
179
206
|
f"url: {APP_URL}\n"
|
|
180
207
|
"license: MIT\n"
|
|
181
208
|
"keywords:\n - process mining\n - Petri nets\n - workflow nets\n - conformance checking\n"
|
|
@@ -35,12 +35,13 @@ from .library import Library, library_for, standard_library
|
|
|
35
35
|
from .record import check, differences, load, save
|
|
36
36
|
from .runner import Cache, Result, Run, Runner
|
|
37
37
|
from .sweep import Sweep
|
|
38
|
-
from .types import (AlignmentResult, Any, CPNet, DFG, Dataset, EventLog, Figure, Footprint, Marking,
|
|
38
|
+
from .types import (Column, AlignmentResult, Any, CPNet, DFG, Dataset, EventLog, Figure, Footprint, Marking,
|
|
39
39
|
PetriNet, Predictions, Predictor, ProcessTree, Regions, ReplayResult, Scores,
|
|
40
40
|
SimpleLog, Table, Text, TransitionSystem, register_type)
|
|
41
41
|
from .workflow import Edge, Group, Node, Workflow, WorkflowError, to_python, workflow
|
|
42
42
|
|
|
43
43
|
__all__ = [
|
|
44
|
+
"Column",
|
|
44
45
|
"AlignmentResult", "Any", "Box", "BoxError", "BoxSpec", "CPNet", "Cache", "DFG", "Dataset", "Edge",
|
|
45
46
|
"EventLog", "Explanation", "Figure", "Footprint", "Group", "Library", "Marking", "Node", "PetriNet",
|
|
46
47
|
"Port", "Predictions", "Predictor", "ProcessTree", "Regions", "ReplayResult", "Result", "Run",
|
|
@@ -18,6 +18,7 @@ parameters whose type is a OpenProcess type input connection points,
|
|
|
18
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
20
|
``Literal["a", "b"]`` a setting that is a choice
|
|
21
|
+
``Column`` a setting that is a column of a connected table
|
|
21
22
|
``Path`` a setting that is a file in the folder
|
|
22
23
|
the docstring the help text
|
|
23
24
|
``inspect.getsource`` the Code tab
|
|
@@ -44,7 +45,7 @@ from dataclasses import dataclass, field
|
|
|
44
45
|
from pathlib import Path
|
|
45
46
|
from typing import Any, Callable, Literal, Union, get_args, get_origin, get_type_hints
|
|
46
47
|
|
|
47
|
-
from .types import TypeInfo, is_known, type_info
|
|
48
|
+
from .types import Column, TypeInfo, is_known, type_info
|
|
48
49
|
|
|
49
50
|
SETTING_TYPES = (int, float, bool, str)
|
|
50
51
|
|
|
@@ -106,7 +107,7 @@ class Setting:
|
|
|
106
107
|
"""A parameter that is not an input: shown as a control in the side panel."""
|
|
107
108
|
|
|
108
109
|
name: str
|
|
109
|
-
kind: str # "int", "float", "bool", "str", "choice", "path"
|
|
110
|
+
kind: str # "int", "float", "bool", "str", "choice", "path", "column"
|
|
110
111
|
default: Any
|
|
111
112
|
choices: tuple = ()
|
|
112
113
|
help: str = ""
|
|
@@ -273,12 +274,14 @@ def _setting_from(name: str, annotation, default, has_default: bool, doc_hints:
|
|
|
273
274
|
kind = "int"
|
|
274
275
|
elif annotation is float:
|
|
275
276
|
kind = "float"
|
|
277
|
+
elif annotation is Column:
|
|
278
|
+
kind = "column"
|
|
276
279
|
elif annotation is str:
|
|
277
280
|
kind = "str"
|
|
278
281
|
else:
|
|
279
282
|
raise BoxError(
|
|
280
283
|
f"parameter {name!r} has type {_type_text(annotation)}, which is neither a OpenProcess "
|
|
281
|
-
f"type (an input) nor int, float, bool, str, Literal[...] or Path (a setting)")
|
|
284
|
+
f"type (an input) nor int, float, bool, str, Literal[...], Column or Path (a setting)")
|
|
282
285
|
if not has_default:
|
|
283
286
|
raise BoxError(f"setting {name!r} needs a default value (e.g. {name}: {kind} = ...)")
|
|
284
287
|
if kind == "float" and isinstance(default, int) and not isinstance(default, bool):
|
|
@@ -17,7 +17,7 @@ from ...mining.log import EventLog
|
|
|
17
17
|
from ...mining.petrinet import PetriNet
|
|
18
18
|
from ..box import box
|
|
19
19
|
from ..convert import convert
|
|
20
|
-
from ..types import Figure, Scores, Table
|
|
20
|
+
from ..types import Column, Figure, Scores, Table
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
@box(name="Describe log", group="Science", needs="pandas")
|
|
@@ -109,12 +109,14 @@ def correlate(table: Table) -> Table:
|
|
|
109
109
|
|
|
110
110
|
@box(name="Plot", group="Science", needs="matplotlib")
|
|
111
111
|
def plot(scores: list[Scores] = (), tables: list[Table] = (),
|
|
112
|
-
x:
|
|
113
|
-
"""Bars per model for scores, histograms for bootstrap tables, or
|
|
114
|
-
|
|
112
|
+
x: Column = "", y: Column = "") -> Figure:
|
|
113
|
+
"""Bars per model for scores, histograms for bootstrap tables, or column
|
|
114
|
+
*y* of a table against column *x*: bars when *x* holds names (a model per
|
|
115
|
+
bar), a line when it holds numbers (a sweep). Once *x* and *y* are set
|
|
116
|
+
they decide the figure, whatever else is connected. The figure is a
|
|
115
117
|
matplotlib figure, converted to SVG; its code is this box's code.
|
|
116
118
|
|
|
117
|
-
x: a column of the table to put on the x axis
|
|
119
|
+
x: a column of the table to put on the x axis
|
|
118
120
|
y: the column to plot against it
|
|
119
121
|
"""
|
|
120
122
|
import matplotlib
|
|
@@ -122,7 +124,24 @@ def plot(scores: list[Scores] = (), tables: list[Table] = (),
|
|
|
122
124
|
import matplotlib.pyplot as plt
|
|
123
125
|
fig, ax = plt.subplots(figsize=(5.5, 3.2))
|
|
124
126
|
scores, tables = list(scores), list(tables)
|
|
125
|
-
if
|
|
127
|
+
if tables and x and y:
|
|
128
|
+
for table in tables:
|
|
129
|
+
for name in (x, y):
|
|
130
|
+
if name not in table.columns:
|
|
131
|
+
raise ValueError(f"{table.name} has no column {name!r}; it has {', '.join(table.columns)}")
|
|
132
|
+
numeric = all(isinstance(v, (int, float)) and not isinstance(v, bool) for t in tables for v in t.column(x))
|
|
133
|
+
for table in tables:
|
|
134
|
+
values = [float(v) for v in table.column(y)]
|
|
135
|
+
if numeric:
|
|
136
|
+
ax.plot(table.column(x), values, marker="o", label=table.name)
|
|
137
|
+
else:
|
|
138
|
+
ax.bar([str(v) for v in table.column(x)], values, label=table.name)
|
|
139
|
+
ax.set_xlabel(x)
|
|
140
|
+
ax.set_ylabel(y)
|
|
141
|
+
if len(tables) > 1:
|
|
142
|
+
ax.legend(fontsize=8)
|
|
143
|
+
caption = f"{y} against {x}."
|
|
144
|
+
elif scores:
|
|
126
145
|
metrics = [m for m in dict.fromkeys(k for s in scores for k in s.metrics)
|
|
127
146
|
if all(isinstance(s.metrics.get(m, 0.0), (int, float)) for s in scores)]
|
|
128
147
|
width = 0.8 / max(1, len(metrics))
|
|
@@ -134,14 +153,6 @@ def plot(scores: list[Scores] = (), tables: list[Table] = (),
|
|
|
134
153
|
ax.set_ylim(0, 1.05)
|
|
135
154
|
ax.legend(fontsize=8)
|
|
136
155
|
caption = f"{len(scores)} models, {len(metrics)} scores each."
|
|
137
|
-
elif tables and x and y:
|
|
138
|
-
for table in tables:
|
|
139
|
-
ax.plot(table.column(x), table.column(y), marker="o", label=table.name)
|
|
140
|
-
ax.set_xlabel(x)
|
|
141
|
-
ax.set_ylabel(y)
|
|
142
|
-
if len(tables) > 1:
|
|
143
|
-
ax.legend(fontsize=8)
|
|
144
|
-
caption = f"{y} against {x}."
|
|
145
156
|
elif tables:
|
|
146
157
|
for table in tables:
|
|
147
158
|
if table.samples:
|
|
@@ -150,7 +161,7 @@ def plot(scores: list[Scores] = (), tables: list[Table] = (),
|
|
|
150
161
|
ax.legend(fontsize=8)
|
|
151
162
|
caption = f"{len(tables)} bootstrap distribution{'s' if len(tables) != 1 else ''}."
|
|
152
163
|
else:
|
|
153
|
-
raise ValueError("Connect scores, or tables with samples or
|
|
164
|
+
raise ValueError("Connect scores, or tables with samples, or a table and set both x and y")
|
|
154
165
|
fig.tight_layout()
|
|
155
166
|
figure = convert(fig, Figure)
|
|
156
167
|
plt.close(fig)
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""Undo and redo for a workflow, by snapshots.
|
|
2
|
+
|
|
3
|
+
A workflow is small (its dict form, :meth:`Workflow.to_dict`, is a few
|
|
4
|
+
kilobytes), so every change keeps the whole state before it: adding,
|
|
5
|
+
removing, wiring, moving or grouping boxes, a setting, a rename. Undo puts
|
|
6
|
+
a snapshot back; the page then runs the boxes again, and the cache serves
|
|
7
|
+
the ones that did not change. Snapshots are taken where the page already
|
|
8
|
+
learns of a change, so no edit is instrumented by hand, and a box author
|
|
9
|
+
never sees any of this.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import time
|
|
15
|
+
|
|
16
|
+
#: How many steps back are kept.
|
|
17
|
+
LIMIT = 100
|
|
18
|
+
#: A drag arrives as several moves; those within this many seconds are one step.
|
|
19
|
+
MOVE_SECONDS = 1.5
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def describe(before: dict, after: dict) -> str:
|
|
23
|
+
"""What changed from ``before`` to ``after``, for "Undo adding a box"."""
|
|
24
|
+
b = {n["id"]: n for n in before.get("boxes", [])}
|
|
25
|
+
a = {n["id"]: n for n in after.get("boxes", [])}
|
|
26
|
+
added, removed = set(a) - set(b), set(b) - set(a)
|
|
27
|
+
if added:
|
|
28
|
+
return "adding a box" if len(added) == 1 else f"adding {len(added)} boxes"
|
|
29
|
+
if removed:
|
|
30
|
+
return "removing a box" if len(removed) == 1 else f"removing {len(removed)} boxes"
|
|
31
|
+
if before.get("connections") != after.get("connections"):
|
|
32
|
+
return "a connection"
|
|
33
|
+
if before.get("groups") != after.get("groups"):
|
|
34
|
+
return "grouping"
|
|
35
|
+
for node_id in a:
|
|
36
|
+
if a[node_id].get("settings") != b[node_id].get("settings"):
|
|
37
|
+
return "a setting"
|
|
38
|
+
if a[node_id].get("title", "") != b[node_id].get("title", ""):
|
|
39
|
+
return "a rename"
|
|
40
|
+
if any(a[i].get("position") != b[i].get("position") for i in a):
|
|
41
|
+
return "a move"
|
|
42
|
+
if any(a[i].get("layout") != b[i].get("layout") for i in a):
|
|
43
|
+
return "tidying a drawing"
|
|
44
|
+
if before.get("name") != after.get("name"):
|
|
45
|
+
return "a rename"
|
|
46
|
+
return "a change"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class History:
|
|
50
|
+
"""The states a workflow went through; :attr:`state` is the current one."""
|
|
51
|
+
|
|
52
|
+
def __init__(self, snapshot: dict, limit: int = LIMIT) -> None:
|
|
53
|
+
self.state = snapshot
|
|
54
|
+
self.limit = limit
|
|
55
|
+
self.undo_stack: list[dict] = []
|
|
56
|
+
self.redo_stack: list[dict] = []
|
|
57
|
+
self._last: tuple[str | None, float] = (None, 0.0)
|
|
58
|
+
|
|
59
|
+
def record(self, snapshot: dict, kind: str = "change", now: float | None = None) -> bool:
|
|
60
|
+
"""The workflow is now ``snapshot``; the state before it becomes undoable.
|
|
61
|
+
Returns whether anything changed. ``kind`` "move" folds a drag's
|
|
62
|
+
steps into one."""
|
|
63
|
+
if snapshot == self.state:
|
|
64
|
+
return False
|
|
65
|
+
now = time.monotonic() if now is None else now
|
|
66
|
+
last_kind, last_time = self._last
|
|
67
|
+
if not (kind == "move" and last_kind == "move" and now - last_time < MOVE_SECONDS and self.undo_stack):
|
|
68
|
+
self.undo_stack.append(self.state)
|
|
69
|
+
del self.undo_stack[:-self.limit]
|
|
70
|
+
self.redo_stack.clear()
|
|
71
|
+
self.state = snapshot
|
|
72
|
+
self._last = (kind, now)
|
|
73
|
+
return True
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def can_undo(self) -> bool:
|
|
77
|
+
return bool(self.undo_stack)
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def can_redo(self) -> bool:
|
|
81
|
+
return bool(self.redo_stack)
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def undo_label(self) -> str:
|
|
85
|
+
return describe(self.undo_stack[-1], self.state) if self.undo_stack else ""
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def redo_label(self) -> str:
|
|
89
|
+
return describe(self.state, self.redo_stack[-1]) if self.redo_stack else ""
|
|
90
|
+
|
|
91
|
+
def undo(self) -> tuple[str, dict] | None:
|
|
92
|
+
"""Step back: (what is undone, the state to restore), or None."""
|
|
93
|
+
if not self.undo_stack:
|
|
94
|
+
return None
|
|
95
|
+
before = self.undo_stack.pop()
|
|
96
|
+
label = describe(before, self.state)
|
|
97
|
+
self.redo_stack.append(self.state)
|
|
98
|
+
self.state = before
|
|
99
|
+
self._last = (None, 0.0)
|
|
100
|
+
return label, before
|
|
101
|
+
|
|
102
|
+
def redo(self) -> tuple[str, dict] | None:
|
|
103
|
+
if not self.redo_stack:
|
|
104
|
+
return None
|
|
105
|
+
after = self.redo_stack.pop()
|
|
106
|
+
label = describe(self.state, after)
|
|
107
|
+
self.undo_stack.append(self.state)
|
|
108
|
+
self.state = after
|
|
109
|
+
self._last = (None, 0.0)
|
|
110
|
+
return label, after
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
__all__ = ["History", "describe", "LIMIT", "MOVE_SECONDS"]
|