openprocess 0.14.0__tar.gz → 0.16.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.14.0/openprocess.egg-info → openprocess-0.16.0}/PKG-INFO +38 -25
- {openprocess-0.14.0 → openprocess-0.16.0}/README.md +37 -24
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/__init__.py +1 -1
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/cli.py +29 -12
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/exercises/pack.md +1 -1
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/__init__.py +1 -1
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/figures.py +1 -1
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/record.py +121 -23
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/report.py +1 -1
- openprocess-0.16.0/openprocess/flow/reproducibility.py +110 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/studio.py +2 -2
- openprocess-0.16.0/openprocess/flow/versions.py +102 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/workflow.py +11 -5
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/flow/page.py +170 -70
- openprocess-0.16.0/openprocess/gui/flow/report.py +537 -0
- openprocess-0.16.0/openprocess/gui/flow/versions.py +188 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/learn/mode.py +8 -3
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/app.py +35 -12
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/documents.py +2 -1
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/dotted_chart.py +9 -2
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/style.py +5 -1
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/workspace.py +7 -2
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/learn/checks.py +1 -1
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/learn/computed.py +1 -1
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/learn/exercise-packs.md +5 -5
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/learn/pack.py +2 -2
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mcp/api.py +9 -8
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mcp/connecting.md +1 -1
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mcp/server.py +5 -5
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/dotted.py +5 -2
- {openprocess-0.14.0 → openprocess-0.16.0/openprocess.egg-info}/PKG-INFO +38 -25
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess.egg-info/SOURCES.txt +3 -2
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_citation.py +3 -2
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_flow.py +11 -5
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_flow_gui.py +312 -59
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_framework_contract.py +39 -5
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_learn.py +1 -1
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_learn_gui.py +1 -1
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_mcp.py +3 -3
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_parallel_boxes.py +2 -2
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_researcher_api.py +2 -2
- openprocess-0.16.0/tests/test_snapshots.py +98 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_studio_gui.py +6 -4
- openprocess-0.14.0/openprocess/flow/reproducibility.py +0 -79
- openprocess-0.14.0/openprocess/gui/flow/runs.py +0 -130
- openprocess-0.14.0/openprocess/gui/flow/summary.py +0 -199
- openprocess-0.14.0/tests/test_snapshots.py +0 -55
- {openprocess-0.14.0 → openprocess-0.16.0}/LICENSE +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/cpnpy/__init__.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/analysis/__init__.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/analysis/state_space.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/analysis/state_space_process.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/citation.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/exercises/1 Petri nets/Exercise 1.1 Order handling/answer.pnml +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/exercises/1 Petri nets/Exercise 1.1 Order handling/question.md +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/exercises/2 Soundness/Exercise 2.1 Spot the flaw/answer.pnml +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/exercises/2 Soundness/Exercise 2.1 Spot the flaw/net.pnml +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/exercises/2 Soundness/Exercise 2.1 Spot the flaw/question.md +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/exercises/3 Discovery/Exercise 3.1 The alpha-algorithm/log.txt +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/exercises/3 Discovery/Exercise 3.1 The alpha-algorithm/question.md +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/exercises/4 Regions/Exercise 4.1 Regions of a transition system/question.md +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/exercises/4 Regions/Exercise 4.1 Regions of a transition system/ts.txt +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/exercises/5 Markings/Exercise 5.1 Markings and matrices/net.pnml +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/exercises/5 Markings/Exercise 5.1 Markings and matrices/question.md +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/exercises/6 Inductive Miner/Exercise 6.1 Cuts and trees/log.txt +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/exercises/6 Inductive Miner/Exercise 6.1 Cuts and trees/question.md +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/log.txt +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/m1.pnml +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/m2.pnml +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/m3.pnml +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/net.pnml +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/exercises/7 Conformance/Exercise 7.1 Replay, alignments and workflows/question.md +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/box.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/boxes/__init__.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/boxes/check.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/boxes/compare.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/boxes/cpn.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/boxes/discover.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/boxes/filter.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/boxes/input.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/boxes/measure.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/boxes/output.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/boxes/predict.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/boxes/science.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/boxes/sweeps.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/changes.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/convert.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/datasets.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/derivation.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/explain.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/history.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/library.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/loading.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/parallel.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/provenance.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/runner.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/script.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/snapshots.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/sweep.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/flow/types.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/__init__.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/app.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/arc_editing.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/canvas.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/flow/__init__.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/flow/canvas.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/flow/picker.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/flow/stepper.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/flow/templates.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/flow/viewers.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/items.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/learn/answer_boxes.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/learn/concealment.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/panning.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/resources/openprocess-icon.png +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/__init__.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/__main__.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/alignment_view.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/charts.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/cite.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/command_palette.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/compare_page.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/connect.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/connections.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/cpn_page.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/definition_view.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/derivation_view.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/file_dialogs.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/filter_dialog.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/graph_builders.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/graph_view.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/instances.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/loading.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/log_editor.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/log_page.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/markdown_view.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/mathtext.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/ml_highlighter.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/model_page.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/motion.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/net_comparison.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/notes_overlay.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/petri_page.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/provenance.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/regions_view.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/remote.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/review.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/sidebar.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/spaces.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/tool_icons.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/updates.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/widgets.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/studio/workers.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/theme.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/gui/tidy.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/io/__init__.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/io/cpn_reader.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/io/cpn_writer.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/learn/__init__.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/learn/answers.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/learn/context.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/learn/exam.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/learn/importer.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/learn/notation.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/learn/sheet.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mcp/__init__.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mcp/__main__.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mcp/install.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/__init__.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/analysis.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/bpmn.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/cases.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/columns.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/compare_nets.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/conformance/__init__.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/conformance/alignments.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/conformance/quality.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/conformance/token_replay.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/csv_import.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/definitions.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/dfg.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/discovery/__init__.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/discovery/alpha.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/discovery/heuristics.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/discovery/inductive.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/discovery/state_regions.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/drawing.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/filtering.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/footprint.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/invariants.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/layout.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/log.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/ocel.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/parallel.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/petrinet.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/playout.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/pm4py_bridge.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/pnml.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/processtree.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/regions.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/stats.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/structure.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/transition_system.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/mining/xes.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/ml/__init__.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/ml/ast_nodes.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/ml/builtins.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/ml/colorsets.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/ml/errors.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/ml/evaluator.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/ml/lexer.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/ml/multiset.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/ml/parser.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/ml/values.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/model/__init__.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/model/declarations.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/model/examples.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/model/net.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/model/plain.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/packages.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/palette.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/parallel.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/references.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/sim/__init__.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/sim/binding.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/sim/export.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/sim/performance.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/sim/simulator.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/teaching/__init__.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/teaching/answers.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/teaching/checks.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/teaching/pack.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess/teaching/sheet.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess.egg-info/dependency_links.txt +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess.egg-info/entry_points.txt +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess.egg-info/requires.txt +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/openprocess.egg-info/top_level.txt +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/pyproject.toml +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/setup.cfg +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_arc_editing.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_bpmn.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_colorsets.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_columns.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_cpn_course_model.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_definitions.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_docs.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_dotted_chart.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_exercises_gui.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_hierarchy.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_history.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_io.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_large_log_page.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_large_logs.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_log_editing.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_mcp_gui.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_measure.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_mining.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_ml.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_multiset.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_ocel.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_packages.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_parallel_and_loading.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_performance.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_regions.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_rename.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_report.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_review_fixes.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_simulation.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_state_space.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_studio_lazy.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_teaching.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_tidy.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_updates.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.0}/tests/test_wf_structure.py +0 -0
- {openprocess-0.14.0 → openprocess-0.16.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.16.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
|
|
@@ -73,7 +73,7 @@ tools (and, until version 0.7, went by the name CPNpy):
|
|
|
73
73
|
| **Petri nets & WF-nets** | Draw nets the way the lectures do. Get a soundness verdict with a counterexample for every violation, and replay it in the token game. Also: behavioural properties, P- and T-invariants, the footprint matrix, the reachability graph, PNML import and export. | WoPeD, ProM, pen and paper |
|
|
74
74
|
| **Process mining** | Import XES or CSV logs, OCEL 2.0 object-centric logs, or type textbook logs like `[<a,b,c>^3, <a,c>^2]`. Filter them, explore variants, the dotted chart and the process map. Discover models (α-algorithm, Inductive Miner, Heuristics Miner, state-based regions). Check conformance (token replay, alignments, precision…) and compare logs. | ProM, Disco |
|
|
75
75
|
| **Coloured Petri nets** | Open, edit and save CPN Tools models (`.cpn`), hierarchical ones included. Step through or simulate them, compute the state space, and export a simulation as an event log to mine. | CPN Tools / CPN IDE |
|
|
76
|
-
| **Workflows** | Boxes on a canvas: a log, a miner, a fitness check, a comparison, a sweep over a setting, a prediction pipeline. Click a box to see its result, *how* it got there (the α-algorithm's eight steps, the replay per variant), its code and its settings. Change a setting and only what follows runs again. Saved as a `.
|
|
76
|
+
| **Workflows** | Boxes on a canvas: a log, a miner, a fitness check, a comparison, a sweep over a setting, a prediction pipeline. Click a box to see its result, *how* it got there (the α-algorithm's eight steps, the replay per variant), its code and its settings. Change a setting and only what follows runs again. Saved as a `.analysis` file with everything needed to get the same numbers back. Your own algorithm is one Python function in a `boxes/` folder. | RapidProM |
|
|
77
77
|
| **Learn** | A mode of its own for worksheets, built on the app: answer in boxes on the sheet (sets, markings, matrices, cuts and trees, alignments, a net in the editor or a workflow on the canvas beside it) and press **Check**. Most answers are checked automatically, often against answers worked out from the given log or net. Packs are plain Markdown, can be exams with a clock and points, and a past exam imports as a skeleton pack. Demo exercises included. | Answer sheets, a notes app and guesswork |
|
|
78
78
|
| **Folders** | Open a folder such as *Week 2*: every log and net in it is listed in the sidebar, with its subfolders. The folder and the app stay in step both ways: new nets and edits are saved into it as you go, and changes made in Finder show up by themselves. | Finder windows and *File ▸ Open* every time |
|
|
79
79
|
|
|
@@ -598,7 +598,7 @@ without distractions, and *Exit* brings your folder back exactly as it was.
|
|
|
598
598
|
writes the marks as CSV. A pack can also give every student a **variant**
|
|
599
599
|
of its own (a log played out from a net with a seed made from their name).
|
|
600
600
|
- **Your work is saved as you go**, in the exercise's folder:
|
|
601
|
-
`my answers.json`, `my answer.pnml` for a net, `my workflow.
|
|
601
|
+
`my answers.json`, `my answer.pnml` for a net, `my workflow.analysis` for a
|
|
602
602
|
workflow and `my notes.md`.
|
|
603
603
|
|
|
604
604
|
**Writing a pack** (for a course or an exam) is plain Markdown: put an
|
|
@@ -791,14 +791,28 @@ Miner, a fitness check), *Compare discovery* (three miners side by side),
|
|
|
791
791
|
confidence* (bootstrap intervals, a test and a plot; needs the `science`
|
|
792
792
|
extra) and *Predict the next activity*. Or start empty.
|
|
793
793
|
|
|
794
|
-
| The canvas: *Compare discovery*, three miners side by side |
|
|
794
|
+
| The canvas: *Compare discovery*, three miners side by side | Report: every box's result, in run order, on one page |
|
|
795
795
|
|---|---|
|
|
796
|
-
|  |  |  |
|
|
797
|
+
|
|
798
|
+
- **An analysis opens on its Report.** Its header has **Report | Canvas |
|
|
799
|
+
Versions**, then **+ Add box**, **Run ▶**, and **⋯** for Run from scratch,
|
|
800
|
+
Keep as version, Record, Export experiment and Save. The sidebar beside it
|
|
801
|
+
lists the folder's analyses and, folded under them, its logs.
|
|
802
|
+
- **Report** is every box's result, in the order the boxes run, on one
|
|
803
|
+
scrolling page: a section per box with the group it is in, its name, the
|
|
804
|
+
line the canvas shows under it, its status, the result as the side panel
|
|
805
|
+
shows it (a table scrolls sideways inside its section when it is wider; a
|
|
806
|
+
figure keeps its proportions; a score gets a key-figure tile), its
|
|
807
|
+
settings as chips, and *Open ›*, *How* and *Code*, which open the box in
|
|
808
|
+
the side panel on the canvas. A slim *Steps* outline on the left lists
|
|
809
|
+
the sections, grouped as the canvas groups the boxes, with each box's
|
|
810
|
+
key figure; a click scrolls to the section, and on a narrow window the
|
|
811
|
+
outline folds into a *Steps* menu. Nothing is computed there: while a
|
|
812
|
+
run goes, each section is redrawn as its box reports; a box that has not
|
|
813
|
+
run says so, a failed one shows its error. The process map is the
|
|
814
|
+
*Directly-follows graph* box's result; an analysis without that box is
|
|
815
|
+
offered one in one click.
|
|
802
816
|
- **+ Add box** (or a double-click on the canvas) opens every box, one
|
|
803
817
|
section per group (Input, Filter, Discover, Check, Measure, Compare, Output,
|
|
804
818
|
then Science, Predict, Coloured nets, Sweep and your own boxes), in a list that
|
|
@@ -812,12 +826,6 @@ extra) and *Predict the next activity*. Or start empty.
|
|
|
812
826
|
notation…* in the sidebar land as boxes on the current analysis (or a new
|
|
813
827
|
one named after the file). In a box's **Settings**, *Choose…* picks the
|
|
814
828
|
file and the list beside it offers only the files the box can read.
|
|
815
|
-
- **Summary** is the key figure of every box, in one place: a tile per box,
|
|
816
|
-
in the order the boxes run (a score's first metric, a table's size, a
|
|
817
|
-
log's cases, a net's places and transitions). Nothing is computed there:
|
|
818
|
-
a tile is the box's result, as on the canvas, and a tile opens the box.
|
|
819
|
-
The process map under the tiles is the *Directly-follows graph* box's
|
|
820
|
-
result; an analysis without that box offers to add it in one click.
|
|
821
829
|
- **Connect** by dragging from the dot on the right of a box: while you drag,
|
|
822
830
|
only the inputs that take that kind of result light up, so a wrong
|
|
823
831
|
connection cannot be made. Click a wire and press Delete to remove it.
|
|
@@ -838,9 +846,14 @@ extra) and *Predict the next activity*. Or start empty.
|
|
|
838
846
|
- **Only what changed runs again.** Every box shows a status dot: waiting,
|
|
839
847
|
running, done, failed (the error is on the Result tab; the rest keeps
|
|
840
848
|
working), or waiting for your OK.
|
|
841
|
-
- **
|
|
842
|
-
|
|
843
|
-
|
|
849
|
+
- **Versions** is where the numbers stand against the record and every
|
|
850
|
+
version kept. The first run with no failure is kept as v1; from then on the
|
|
851
|
+
badge beside the title says *v1 · reproducible*, *v1 · 2 changes* or
|
|
852
|
+
*Changed since v1*, and **Keep as version…** records the run as v2 with a
|
|
853
|
+
note. Any version can be compared with now (the boxes whose result differs,
|
|
854
|
+
first) or restored whole. **Record** shows the workflow as Python and the
|
|
855
|
+
file; **Run from scratch** says what changed since the version was kept
|
|
856
|
+
before computing everything again.
|
|
844
857
|
- **Your own boxes**: a `.py` file in the folder's `boxes/` subfolder (see
|
|
845
858
|
below). The app asks once per folder before running them, and reloads a
|
|
846
859
|
box when its file is saved.
|
|
@@ -894,15 +907,15 @@ extra) and *Predict the next activity*. Or start empty.
|
|
|
894
907
|
`~/Library/Application Support/OpenProcess/packages`), and the boxes that
|
|
895
908
|
need the package come alive without a restart. PM4Py needs a Python
|
|
896
909
|
install (`pip install pm4py`), as parts of it are built from source.
|
|
897
|
-
- **Motion** is kept short: a space switch, the side panel and
|
|
898
|
-
|
|
910
|
+
- **Motion** is kept short: a space switch, the side panel and Report ↔
|
|
911
|
+
Canvas settle in a fifth of a second, and a box pulses once when it
|
|
899
912
|
finishes. `OPENPROCESS_NO_MOTION=1` turns it off.
|
|
900
913
|
|
|
901
914
|
## Workflows from Python
|
|
902
915
|
|
|
903
916
|
Every algorithm is also a **box**: a Python function with type hints that
|
|
904
917
|
the app (from 0.7) draws on a canvas. A workflow of boxes runs from Python
|
|
905
|
-
or from the command line, and its `.
|
|
918
|
+
or from the command line, and its `.analysis` file records what is needed
|
|
906
919
|
to get the same numbers again: fingerprints of the input files and of the
|
|
907
920
|
box code, every seed, and the installed packages.
|
|
908
921
|
|
|
@@ -920,12 +933,12 @@ wf.connect(log, model); wf.connect(model, fit, "model"); wf.connect(log, fit, "l
|
|
|
920
933
|
run = Runner().run(wf)
|
|
921
934
|
print(run.value(fit).metrics) # fitness, precision, generalisation, simplicity
|
|
922
935
|
print(run.result(model).explanation.steps) # how the Inductive Miner got there
|
|
923
|
-
save(wf, "orders.
|
|
936
|
+
save(wf, "orders.analysis", run)
|
|
924
937
|
```
|
|
925
938
|
|
|
926
939
|
```bash
|
|
927
|
-
openprocess run orders.
|
|
928
|
-
openprocess run orders.
|
|
940
|
+
openprocess run orders.analysis --check # re-run; fails if any result differs from the record
|
|
941
|
+
openprocess run orders.analysis --sweep noise=0..0.5 step 0.1
|
|
929
942
|
openprocess boxes # every box (listed in docs/boxes.md), with a folder's boxes/
|
|
930
943
|
openprocess datasets list # the BPI Challenge, Sepsis, ... logs by name
|
|
931
944
|
```
|
|
@@ -31,7 +31,7 @@ tools (and, until version 0.7, went by the name CPNpy):
|
|
|
31
31
|
| **Petri nets & WF-nets** | Draw nets the way the lectures do. Get a soundness verdict with a counterexample for every violation, and replay it in the token game. Also: behavioural properties, P- and T-invariants, the footprint matrix, the reachability graph, PNML import and export. | WoPeD, ProM, pen and paper |
|
|
32
32
|
| **Process mining** | Import XES or CSV logs, OCEL 2.0 object-centric logs, or type textbook logs like `[<a,b,c>^3, <a,c>^2]`. Filter them, explore variants, the dotted chart and the process map. Discover models (α-algorithm, Inductive Miner, Heuristics Miner, state-based regions). Check conformance (token replay, alignments, precision…) and compare logs. | ProM, Disco |
|
|
33
33
|
| **Coloured Petri nets** | Open, edit and save CPN Tools models (`.cpn`), hierarchical ones included. Step through or simulate them, compute the state space, and export a simulation as an event log to mine. | CPN Tools / CPN IDE |
|
|
34
|
-
| **Workflows** | Boxes on a canvas: a log, a miner, a fitness check, a comparison, a sweep over a setting, a prediction pipeline. Click a box to see its result, *how* it got there (the α-algorithm's eight steps, the replay per variant), its code and its settings. Change a setting and only what follows runs again. Saved as a `.
|
|
34
|
+
| **Workflows** | Boxes on a canvas: a log, a miner, a fitness check, a comparison, a sweep over a setting, a prediction pipeline. Click a box to see its result, *how* it got there (the α-algorithm's eight steps, the replay per variant), its code and its settings. Change a setting and only what follows runs again. Saved as a `.analysis` file with everything needed to get the same numbers back. Your own algorithm is one Python function in a `boxes/` folder. | RapidProM |
|
|
35
35
|
| **Learn** | A mode of its own for worksheets, built on the app: answer in boxes on the sheet (sets, markings, matrices, cuts and trees, alignments, a net in the editor or a workflow on the canvas beside it) and press **Check**. Most answers are checked automatically, often against answers worked out from the given log or net. Packs are plain Markdown, can be exams with a clock and points, and a past exam imports as a skeleton pack. Demo exercises included. | Answer sheets, a notes app and guesswork |
|
|
36
36
|
| **Folders** | Open a folder such as *Week 2*: every log and net in it is listed in the sidebar, with its subfolders. The folder and the app stay in step both ways: new nets and edits are saved into it as you go, and changes made in Finder show up by themselves. | Finder windows and *File ▸ Open* every time |
|
|
37
37
|
|
|
@@ -556,7 +556,7 @@ without distractions, and *Exit* brings your folder back exactly as it was.
|
|
|
556
556
|
writes the marks as CSV. A pack can also give every student a **variant**
|
|
557
557
|
of its own (a log played out from a net with a seed made from their name).
|
|
558
558
|
- **Your work is saved as you go**, in the exercise's folder:
|
|
559
|
-
`my answers.json`, `my answer.pnml` for a net, `my workflow.
|
|
559
|
+
`my answers.json`, `my answer.pnml` for a net, `my workflow.analysis` for a
|
|
560
560
|
workflow and `my notes.md`.
|
|
561
561
|
|
|
562
562
|
**Writing a pack** (for a course or an exam) is plain Markdown: put an
|
|
@@ -749,14 +749,28 @@ Miner, a fitness check), *Compare discovery* (three miners side by side),
|
|
|
749
749
|
confidence* (bootstrap intervals, a test and a plot; needs the `science`
|
|
750
750
|
extra) and *Predict the next activity*. Or start empty.
|
|
751
751
|
|
|
752
|
-
| The canvas: *Compare discovery*, three miners side by side |
|
|
752
|
+
| The canvas: *Compare discovery*, three miners side by side | Report: every box's result, in run order, on one page |
|
|
753
753
|
|---|---|
|
|
754
|
-
|  |  |  |
|
|
755
|
+
|
|
756
|
+
- **An analysis opens on its Report.** Its header has **Report | Canvas |
|
|
757
|
+
Versions**, then **+ Add box**, **Run ▶**, and **⋯** for Run from scratch,
|
|
758
|
+
Keep as version, Record, Export experiment and Save. The sidebar beside it
|
|
759
|
+
lists the folder's analyses and, folded under them, its logs.
|
|
760
|
+
- **Report** is every box's result, in the order the boxes run, on one
|
|
761
|
+
scrolling page: a section per box with the group it is in, its name, the
|
|
762
|
+
line the canvas shows under it, its status, the result as the side panel
|
|
763
|
+
shows it (a table scrolls sideways inside its section when it is wider; a
|
|
764
|
+
figure keeps its proportions; a score gets a key-figure tile), its
|
|
765
|
+
settings as chips, and *Open ›*, *How* and *Code*, which open the box in
|
|
766
|
+
the side panel on the canvas. A slim *Steps* outline on the left lists
|
|
767
|
+
the sections, grouped as the canvas groups the boxes, with each box's
|
|
768
|
+
key figure; a click scrolls to the section, and on a narrow window the
|
|
769
|
+
outline folds into a *Steps* menu. Nothing is computed there: while a
|
|
770
|
+
run goes, each section is redrawn as its box reports; a box that has not
|
|
771
|
+
run says so, a failed one shows its error. The process map is the
|
|
772
|
+
*Directly-follows graph* box's result; an analysis without that box is
|
|
773
|
+
offered one in one click.
|
|
760
774
|
- **+ Add box** (or a double-click on the canvas) opens every box, one
|
|
761
775
|
section per group (Input, Filter, Discover, Check, Measure, Compare, Output,
|
|
762
776
|
then Science, Predict, Coloured nets, Sweep and your own boxes), in a list that
|
|
@@ -770,12 +784,6 @@ extra) and *Predict the next activity*. Or start empty.
|
|
|
770
784
|
notation…* in the sidebar land as boxes on the current analysis (or a new
|
|
771
785
|
one named after the file). In a box's **Settings**, *Choose…* picks the
|
|
772
786
|
file and the list beside it offers only the files the box can read.
|
|
773
|
-
- **Summary** is the key figure of every box, in one place: a tile per box,
|
|
774
|
-
in the order the boxes run (a score's first metric, a table's size, a
|
|
775
|
-
log's cases, a net's places and transitions). Nothing is computed there:
|
|
776
|
-
a tile is the box's result, as on the canvas, and a tile opens the box.
|
|
777
|
-
The process map under the tiles is the *Directly-follows graph* box's
|
|
778
|
-
result; an analysis without that box offers to add it in one click.
|
|
779
787
|
- **Connect** by dragging from the dot on the right of a box: while you drag,
|
|
780
788
|
only the inputs that take that kind of result light up, so a wrong
|
|
781
789
|
connection cannot be made. Click a wire and press Delete to remove it.
|
|
@@ -796,9 +804,14 @@ extra) and *Predict the next activity*. Or start empty.
|
|
|
796
804
|
- **Only what changed runs again.** Every box shows a status dot: waiting,
|
|
797
805
|
running, done, failed (the error is on the Result tab; the rest keeps
|
|
798
806
|
working), or waiting for your OK.
|
|
799
|
-
- **
|
|
800
|
-
|
|
801
|
-
|
|
807
|
+
- **Versions** is where the numbers stand against the record and every
|
|
808
|
+
version kept. The first run with no failure is kept as v1; from then on the
|
|
809
|
+
badge beside the title says *v1 · reproducible*, *v1 · 2 changes* or
|
|
810
|
+
*Changed since v1*, and **Keep as version…** records the run as v2 with a
|
|
811
|
+
note. Any version can be compared with now (the boxes whose result differs,
|
|
812
|
+
first) or restored whole. **Record** shows the workflow as Python and the
|
|
813
|
+
file; **Run from scratch** says what changed since the version was kept
|
|
814
|
+
before computing everything again.
|
|
802
815
|
- **Your own boxes**: a `.py` file in the folder's `boxes/` subfolder (see
|
|
803
816
|
below). The app asks once per folder before running them, and reloads a
|
|
804
817
|
box when its file is saved.
|
|
@@ -852,15 +865,15 @@ extra) and *Predict the next activity*. Or start empty.
|
|
|
852
865
|
`~/Library/Application Support/OpenProcess/packages`), and the boxes that
|
|
853
866
|
need the package come alive without a restart. PM4Py needs a Python
|
|
854
867
|
install (`pip install pm4py`), as parts of it are built from source.
|
|
855
|
-
- **Motion** is kept short: a space switch, the side panel and
|
|
856
|
-
|
|
868
|
+
- **Motion** is kept short: a space switch, the side panel and Report ↔
|
|
869
|
+
Canvas settle in a fifth of a second, and a box pulses once when it
|
|
857
870
|
finishes. `OPENPROCESS_NO_MOTION=1` turns it off.
|
|
858
871
|
|
|
859
872
|
## Workflows from Python
|
|
860
873
|
|
|
861
874
|
Every algorithm is also a **box**: a Python function with type hints that
|
|
862
875
|
the app (from 0.7) draws on a canvas. A workflow of boxes runs from Python
|
|
863
|
-
or from the command line, and its `.
|
|
876
|
+
or from the command line, and its `.analysis` file records what is needed
|
|
864
877
|
to get the same numbers again: fingerprints of the input files and of the
|
|
865
878
|
box code, every seed, and the installed packages.
|
|
866
879
|
|
|
@@ -878,12 +891,12 @@ wf.connect(log, model); wf.connect(model, fit, "model"); wf.connect(log, fit, "l
|
|
|
878
891
|
run = Runner().run(wf)
|
|
879
892
|
print(run.value(fit).metrics) # fitness, precision, generalisation, simplicity
|
|
880
893
|
print(run.result(model).explanation.steps) # how the Inductive Miner got there
|
|
881
|
-
save(wf, "orders.
|
|
894
|
+
save(wf, "orders.analysis", run)
|
|
882
895
|
```
|
|
883
896
|
|
|
884
897
|
```bash
|
|
885
|
-
openprocess run orders.
|
|
886
|
-
openprocess run orders.
|
|
898
|
+
openprocess run orders.analysis --check # re-run; fails if any result differs from the record
|
|
899
|
+
openprocess run orders.analysis --sweep noise=0..0.5 step 0.1
|
|
887
900
|
openprocess boxes # every box (listed in docs/boxes.md), with a folder's boxes/
|
|
888
901
|
openprocess datasets list # the BPI Challenge, Sepsis, ... logs by name
|
|
889
902
|
```
|
|
@@ -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.16.0"
|
|
52
52
|
|
|
53
53
|
__all__ = [
|
|
54
54
|
"Arc", "BindingElement", "CPNet", "Marking", "Multiset", "Page", "Place",
|
|
@@ -19,7 +19,7 @@ Commands
|
|
|
19
19
|
``stats``, ``filter``, ``discover``, ``conform``,
|
|
20
20
|
``soundness``, ``invariants``
|
|
21
21
|
``exercises`` ``check``, ``marks``, ``import``, ``computes``: exercise packs (OpenProcess Learn)
|
|
22
|
-
``run`` run a ``.
|
|
22
|
+
``run`` run a ``.analysis`` workflow headless; ``--check`` re-runs and
|
|
23
23
|
compares with the record, ``--sweep`` varies a setting,
|
|
24
24
|
``--lock`` writes the requirements the record names,
|
|
25
25
|
``--export`` and ``--report`` write a zip and an HTML page
|
|
@@ -447,12 +447,19 @@ def command_exercises_computes(arguments: argparse.Namespace) -> int:
|
|
|
447
447
|
# ---------------------------------------------------------------------------
|
|
448
448
|
def command_run(arguments: argparse.Namespace) -> int:
|
|
449
449
|
"""Run a workflow file without the app, and say what every box gave."""
|
|
450
|
-
from .flow import Runner, Sweep, check, differences, library_for,
|
|
451
|
-
from .flow.record import requirements_lock
|
|
450
|
+
from .flow import Runner, Sweep, check, differences, library_for, save
|
|
451
|
+
from .flow.record import load_all, requirements_lock
|
|
452
452
|
path = Path(arguments.workflow)
|
|
453
453
|
folder = path.parent
|
|
454
454
|
library = library_for(folder)
|
|
455
|
-
workflow, record =
|
|
455
|
+
workflow, record, versions = load_all(path, library)
|
|
456
|
+
if arguments.against is not None:
|
|
457
|
+
version = versions.get(arguments.against)
|
|
458
|
+
if version is None:
|
|
459
|
+
print(f"openprocess: {path.name} has no version {arguments.against} "
|
|
460
|
+
f"(it has {', '.join(f'v{v.number}' for v in versions.items) or 'none'})", file=sys.stderr)
|
|
461
|
+
return 2
|
|
462
|
+
record = version.record
|
|
456
463
|
if arguments.lock:
|
|
457
464
|
target = folder / "requirements.lock"
|
|
458
465
|
target.write_text(requirements_lock(record), encoding="utf-8")
|
|
@@ -504,6 +511,8 @@ def command_run(arguments: argparse.Namespace) -> int:
|
|
|
504
511
|
if arguments.report:
|
|
505
512
|
from .flow.report import write_report
|
|
506
513
|
print(f"Wrote {write_report(arguments.report, workflow=workflow, run=run, folder=folder)}")
|
|
514
|
+
against = f"v{arguments.against}" if arguments.against is not None else (
|
|
515
|
+
f"v{versions.latest.number}" if versions.latest else "the record")
|
|
507
516
|
if arguments.check:
|
|
508
517
|
if not record.results:
|
|
509
518
|
# Nothing to compare is not a pass: a CI job would claim a reproduction that never happened.
|
|
@@ -513,12 +522,18 @@ def command_run(arguments: argparse.Namespace) -> int:
|
|
|
513
522
|
report = check(workflow, record, run, complete=True)
|
|
514
523
|
if report:
|
|
515
524
|
for line in report:
|
|
516
|
-
print(f"DIFFERS: {line}")
|
|
525
|
+
print(f"DIFFERS from {against}: {line}")
|
|
517
526
|
return 1
|
|
518
|
-
print(f"Every recorded
|
|
527
|
+
print(f"Every result recorded in {against} was reproduced ({len(record.results)} boxes).")
|
|
519
528
|
elif arguments.save:
|
|
520
|
-
|
|
521
|
-
|
|
529
|
+
if run.failed():
|
|
530
|
+
save(workflow, path, run, folder, previous=record, versions=versions)
|
|
531
|
+
print(f"A box failed, so {path.name} keeps " + (f"{against} as its record" if record.results
|
|
532
|
+
else "no record yet"))
|
|
533
|
+
else:
|
|
534
|
+
save(workflow, path, run, folder, previous=record, versions=versions, keep=arguments.note or "")
|
|
535
|
+
print(f"Kept the results as v{versions.latest.number} in {path.name}"
|
|
536
|
+
+ (f": {arguments.note}" if arguments.note else ""))
|
|
522
537
|
return 0 if run.ok else 1
|
|
523
538
|
|
|
524
539
|
|
|
@@ -710,11 +725,13 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
710
725
|
"computes", help="list every compute: an answer block may use")
|
|
711
726
|
exercise_computes.set_defaults(handler=command_exercises_computes)
|
|
712
727
|
|
|
713
|
-
run = subparsers.add_parser("run", help="run a .
|
|
714
|
-
run.add_argument("workflow", help="the .
|
|
728
|
+
run = subparsers.add_parser("run", help="run a .analysis workflow without the app")
|
|
729
|
+
run.add_argument("workflow", help="the .analysis file")
|
|
715
730
|
run.add_argument("--check", action="store_true",
|
|
716
|
-
help="re-run and fail if any result differs from the recorded one")
|
|
717
|
-
run.add_argument("--
|
|
731
|
+
help="re-run and fail if any result differs from the recorded one (the latest version)")
|
|
732
|
+
run.add_argument("--against", type=int, metavar="N", help="check against version N instead of the latest")
|
|
733
|
+
run.add_argument("--save", action="store_true", help="keep the results in the file as the next version")
|
|
734
|
+
run.add_argument("--note", metavar="TEXT", help="with --save: the version's note (what changed, or why)")
|
|
718
735
|
run.add_argument("--sweep", action="append", metavar="SETTING=VALUES",
|
|
719
736
|
help="vary a setting, e.g. noise=0..0.5 step 0.1 or n3.seed=1,2,3")
|
|
720
737
|
run.add_argument("--lock", action="store_true",
|
|
@@ -7,7 +7,7 @@ straight away, and the app never shows you a result you are meant to work out
|
|
|
7
7
|
until you ask for it.
|
|
8
8
|
|
|
9
9
|
Your answers are saved in each exercise's folder (*my answers.json*, and
|
|
10
|
-
*my answer.pnml* for a net you draw, *my workflow.
|
|
10
|
+
*my answer.pnml* for a net you draw, *my workflow.analysis* for a workflow
|
|
11
11
|
you build), so you can stop and carry on later.
|
|
12
12
|
|
|
13
13
|
To write your own pack, copy this folder and change the `question.md` files:
|
|
@@ -24,7 +24,7 @@ Python::
|
|
|
24
24
|
wf.connect(log, model); wf.connect(model, fit, "model"); wf.connect(log, fit, "log")
|
|
25
25
|
run = Runner().run(wf)
|
|
26
26
|
print(run.value(fit).metrics)
|
|
27
|
-
save(wf, "demo.
|
|
27
|
+
save(wf, "demo.analysis", run) # with the record that makes it reproducible
|
|
28
28
|
|
|
29
29
|
Work over many independent items (variants, cases, resamples) runs on all
|
|
30
30
|
cores with :func:`parallel_map`, with exactly the result of a loop.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""The key figure of a result: one number and a line, for a tile or a report.
|
|
2
2
|
|
|
3
|
-
Shared by the
|
|
3
|
+
Shared by the Report (:mod:`openprocess.gui.flow.report`) and the HTML
|
|
4
4
|
report (:mod:`openprocess.flow.report`), so both say the same thing about a
|
|
5
5
|
result: a score's first metric, a table's size, a log's cases, a net's
|
|
6
6
|
places and transitions, a map's activities and paths.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
"""The ``.
|
|
1
|
+
"""The ``.analysis`` file: a workflow, and the record that makes it reproducible.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
connections, the groups,
|
|
3
|
+
An analysis file is JSON: the boxes, their settings and positions, the
|
|
4
|
+
connections, the groups, its **versions** (:mod:`.versions`: the record as
|
|
5
|
+
it was each time the user kept it) and the **record** of the latest version:
|
|
5
6
|
|
|
6
7
|
* the versions of OpenProcess, Python and the optional libraries;
|
|
7
8
|
* the environment: every installed package and its version (the lock);
|
|
@@ -34,7 +35,44 @@ from .runner import DONE, Run
|
|
|
34
35
|
from .types import Dataset, Figure, Predictions, Scores, Table, Text
|
|
35
36
|
from .workflow import Workflow
|
|
36
37
|
|
|
37
|
-
FORMAT = "
|
|
38
|
+
FORMAT = "analysis/1"
|
|
39
|
+
|
|
40
|
+
#: The extension of an analysis file, and the ones it had before (``.cpnflow``,
|
|
41
|
+
#: from when the app was CPNpy: the content is the same, only the name changed).
|
|
42
|
+
EXTENSION = ".analysis"
|
|
43
|
+
OLD_EXTENSIONS = (".cpnflow",)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def is_analysis_file(path) -> bool:
|
|
47
|
+
"""Whether ``path`` is an analysis file by its name, under either extension."""
|
|
48
|
+
return str(path).lower().endswith((EXTENSION,) + OLD_EXTENSIONS)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def analysis_name(path) -> Path:
|
|
52
|
+
"""``path`` under the current extension (``orders.cpnflow`` → ``orders.analysis``)."""
|
|
53
|
+
path = Path(path)
|
|
54
|
+
return path.with_name(path.stem + EXTENSION) if path.suffix.lower() in OLD_EXTENSIONS else path
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def carry_over(folder, depth: int = 3) -> list[Path]:
|
|
58
|
+
"""Rename every ``.cpnflow`` under ``folder`` to ``.analysis`` (the content is
|
|
59
|
+
read either way); a file whose new name is taken is left. Returns the new paths."""
|
|
60
|
+
renamed = []
|
|
61
|
+
root = Path(folder)
|
|
62
|
+
for old in OLD_EXTENSIONS:
|
|
63
|
+
for source in sorted(root.rglob(f"*{old}")):
|
|
64
|
+
if len(source.relative_to(root).parts) - 1 > depth or any(
|
|
65
|
+
part.startswith(".") for part in source.relative_to(root).parts):
|
|
66
|
+
continue
|
|
67
|
+
target = analysis_name(source)
|
|
68
|
+
if target.exists():
|
|
69
|
+
continue
|
|
70
|
+
try:
|
|
71
|
+
source.replace(target)
|
|
72
|
+
except OSError:
|
|
73
|
+
continue
|
|
74
|
+
renamed.append(target)
|
|
75
|
+
return renamed
|
|
38
76
|
|
|
39
77
|
|
|
40
78
|
# ---------------------------------------------------------------------------
|
|
@@ -242,8 +280,11 @@ def canonical(value: Any, _active: set[int] | None = None) -> Any:
|
|
|
242
280
|
if isinstance(value, (list, tuple)):
|
|
243
281
|
return [canonical(v, _active) for v in value]
|
|
244
282
|
if dataclasses.is_dataclass(value) and not isinstance(value, type):
|
|
283
|
+
# A field named with a leading underscore is a cache or a working
|
|
284
|
+
# value (a DFG's per-arc means and medians, say), not content.
|
|
245
285
|
fields = {f.name: canonical(getattr(value, f.name, None), _active)
|
|
246
|
-
for f in dataclasses.fields(value)
|
|
286
|
+
for f in dataclasses.fields(value)
|
|
287
|
+
if not f.name.startswith("_") and f.name not in _NOT_CONTENT}
|
|
247
288
|
return {"type": type(value).__name__, **fields}
|
|
248
289
|
attributes = getattr(value, "__dict__", None)
|
|
249
290
|
if isinstance(attributes, dict):
|
|
@@ -456,14 +497,46 @@ def differences(record: Record, workflow: Workflow, folder: str | Path | None =
|
|
|
456
497
|
# Files
|
|
457
498
|
# ---------------------------------------------------------------------------
|
|
458
499
|
def save(workflow: Workflow, path: str | Path, run: Run | None = None,
|
|
459
|
-
folder: str | Path | None = None, lock: bool = True, history: list[dict] | None = None
|
|
460
|
-
|
|
461
|
-
|
|
500
|
+
folder: str | Path | None = None, lock: bool = True, history: list[dict] | None = None,
|
|
501
|
+
previous: Record | None = None, versions: "Versions | None" = None,
|
|
502
|
+
keep: str | None = None) -> Record:
|
|
503
|
+
"""Write the workflow, its versions and its record to ``path``; returns the record.
|
|
504
|
+
|
|
505
|
+
``history``: the analysis's runs (see :mod:`.snapshots`), kept in the file.
|
|
506
|
+
``previous``: the file's record so far, and ``versions`` the versions it
|
|
507
|
+
holds; see :func:`carried`. ``keep``: a note, to keep this run as a new
|
|
508
|
+
version (``""`` for a version without one): the record becomes this run's.
|
|
509
|
+
Without ``keep`` the record stays as it was, except that the first run in
|
|
510
|
+
which no box fails is kept as version 1 by itself, so a new analysis is
|
|
511
|
+
recorded without a word.
|
|
512
|
+
|
|
513
|
+
``versions`` is updated in place when a version is kept."""
|
|
514
|
+
from .versions import Versions
|
|
462
515
|
path = Path(path)
|
|
463
516
|
folder = folder if folder is not None else path.parent
|
|
464
|
-
|
|
517
|
+
versions = versions if versions is not None else Versions()
|
|
518
|
+
if keep is not None and (run is None or run.failed()):
|
|
519
|
+
raise ValueError("A version keeps a run in which no box failed")
|
|
520
|
+
if keep is None and previous is not None and previous.results:
|
|
521
|
+
record = carried(previous, workflow)
|
|
522
|
+
elif keep is None and (run is None or run.failed()):
|
|
523
|
+
record = make_record(workflow, None, folder, lock) # nothing to keep yet
|
|
524
|
+
else:
|
|
525
|
+
record = make_record(workflow, run, folder, lock)
|
|
526
|
+
if previous is not None and previous.scheme == record.scheme:
|
|
527
|
+
# A box that could not run here (a library it needs is missing)
|
|
528
|
+
# keeps the fingerprint recorded where it could.
|
|
529
|
+
still = {k: v for k, v in previous.results.items() if k in workflow.nodes}
|
|
530
|
+
record.results = {**still, **record.results}
|
|
531
|
+
for entry in record.inputs:
|
|
532
|
+
if entry["sha256"] == "missing" and previous is not None:
|
|
533
|
+
then = next((i for i in previous.inputs if i["node"] == entry["node"]
|
|
534
|
+
and i["setting"] == entry["setting"] and i["file"] == entry["file"]), None)
|
|
535
|
+
if then is not None:
|
|
536
|
+
entry["sha256"] = then["sha256"]
|
|
537
|
+
versions.keep(workflow, run, record, keep or ("First run" if not versions.items else ""))
|
|
465
538
|
record.history = list(history or [])
|
|
466
|
-
data = {"format": FORMAT, **workflow.to_dict(), "record": record.to_dict()}
|
|
539
|
+
data = {"format": FORMAT, **workflow.to_dict(), "versions": versions.to_list(), "record": record.to_dict()}
|
|
467
540
|
text = json.dumps(data, indent=2, ensure_ascii=False, default=str)
|
|
468
541
|
temporary = path.with_name(path.name + ".tmp")
|
|
469
542
|
temporary.write_text(text, encoding="utf-8")
|
|
@@ -471,13 +544,34 @@ def save(workflow: Workflow, path: str | Path, run: Run | None = None,
|
|
|
471
544
|
return record
|
|
472
545
|
|
|
473
546
|
|
|
547
|
+
def carried(previous: Record, workflow: Workflow) -> Record:
|
|
548
|
+
"""The record to write when no version is kept: the previous one as it
|
|
549
|
+
was, results of boxes since removed aside. Saving as you work never
|
|
550
|
+
replaces the record; keeping a version does (:func:`save` with ``keep``)."""
|
|
551
|
+
still = {k: v for k, v in previous.results.items() if k in workflow.nodes}
|
|
552
|
+
return Record(**{**previous.__dict__, "results": still})
|
|
553
|
+
|
|
554
|
+
|
|
474
555
|
def load(path: str | Path, library=None) -> tuple[Workflow, Record]:
|
|
475
|
-
"""Read
|
|
556
|
+
"""Read an analysis file (``.analysis``, or ``.cpnflow`` as it was called)."""
|
|
557
|
+
workflow, record, _versions = load_all(path, library)
|
|
558
|
+
return workflow, record
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
def load_all(path: str | Path, library=None) -> "tuple[Workflow, Record, Versions]":
|
|
562
|
+
"""Read an analysis file: its workflow, the record of its latest version, and
|
|
563
|
+
every version kept. A file from before versions (a record, no list) gives
|
|
564
|
+
its record as version 1."""
|
|
565
|
+
from .versions import Versions
|
|
476
566
|
data = json.loads(Path(path).read_text(encoding="utf-8"))
|
|
477
|
-
if data.get("format", FORMAT).split("/")[0]
|
|
478
|
-
raise ValueError(f"{path} is not
|
|
567
|
+
if data.get("format", FORMAT).split("/")[0] not in ("analysis", "cpnflow"):
|
|
568
|
+
raise ValueError(f"{path} is not an analysis file")
|
|
479
569
|
workflow = Workflow.from_dict(data, library)
|
|
480
|
-
|
|
570
|
+
record = Record.from_dict(data.get("record"))
|
|
571
|
+
versions = Versions.from_list(data.get("versions"))
|
|
572
|
+
if not versions.items and record.results:
|
|
573
|
+
versions = Versions.first_from(workflow, record)
|
|
574
|
+
return workflow, record, versions
|
|
481
575
|
|
|
482
576
|
|
|
483
577
|
def check(workflow: Workflow, record: Record, run: Run, complete: bool = False) -> list[str]:
|
|
@@ -547,7 +641,7 @@ def export_experiment(workflow: Workflow, run: Run | None, target: str | Path,
|
|
|
547
641
|
name = workflow.name or "workflow"
|
|
548
642
|
lines = [f"# {name}", "", f"Exported {record.saved} with OpenProcess {record.versions.get('openprocess', '')}, "
|
|
549
643
|
f"Python {record.versions.get('python', '')}.", "",
|
|
550
|
-
"Run it again with `openprocess run " + f"{name}
|
|
644
|
+
"Run it again with `openprocess run " + f"{name}{EXTENSION} --check`: it fails if any result differs "
|
|
551
645
|
"from the recorded one. `requirements.lock` is the environment it ran in. `run.py` is the same "
|
|
552
646
|
"analysis as a plain Python script (`python run.py`). Or drop this zip on OpenProcess: it unpacks "
|
|
553
647
|
"it into a folder and runs it.", "", "## Boxes", ""]
|
|
@@ -575,7 +669,7 @@ def export_experiment(workflow: Workflow, run: Run | None, target: str | Path,
|
|
|
575
669
|
lines += ["", "## How to cite", "", citation.app_text(), "",
|
|
576
670
|
"`CITATIONS.bib` has this entry and the papers the algorithms follow, as BibTeX."]
|
|
577
671
|
with zipfile.ZipFile(target, "w", zipfile.ZIP_DEFLATED) as zipped:
|
|
578
|
-
zipped.writestr(f"{name}
|
|
672
|
+
zipped.writestr(f"{name}{EXTENSION}", json.dumps(data, indent=2, ensure_ascii=False, default=str))
|
|
579
673
|
zipped.writestr("requirements.lock", requirements_lock(record))
|
|
580
674
|
from .script import script_text
|
|
581
675
|
zipped.writestr("run.py", script_text(packed_workflow, "run.py"))
|
|
@@ -611,11 +705,11 @@ def _describe(value) -> str:
|
|
|
611
705
|
|
|
612
706
|
|
|
613
707
|
def is_experiment(path) -> bool:
|
|
614
|
-
"""Whether ``path`` is a zip :func:`export_experiment` wrote: one
|
|
708
|
+
"""Whether ``path`` is a zip :func:`export_experiment` wrote: one analysis file at the top."""
|
|
615
709
|
import zipfile
|
|
616
710
|
try:
|
|
617
711
|
with zipfile.ZipFile(path) as zipped:
|
|
618
|
-
return sum(1 for n in zipped.namelist() if n
|
|
712
|
+
return sum(1 for n in zipped.namelist() if is_analysis_file(n) and "/" not in n) == 1
|
|
619
713
|
except (OSError, zipfile.BadZipFile):
|
|
620
714
|
return False
|
|
621
715
|
|
|
@@ -630,9 +724,9 @@ def import_experiment(archive, into) -> Path:
|
|
|
630
724
|
archive = Path(archive)
|
|
631
725
|
with zipfile.ZipFile(archive) as zipped:
|
|
632
726
|
names = zipped.namelist()
|
|
633
|
-
flows = [n for n in names if n
|
|
727
|
+
flows = [n for n in names if is_analysis_file(n) and "/" not in n]
|
|
634
728
|
if len(flows) != 1:
|
|
635
|
-
raise ValueError(f"{archive.name} is not an exported experiment (one
|
|
729
|
+
raise ValueError(f"{archive.name} is not an exported experiment (one {EXTENSION} file at the top is needed)")
|
|
636
730
|
folder = Path(into) / archive.stem
|
|
637
731
|
counter = 2
|
|
638
732
|
while folder.exists():
|
|
@@ -648,6 +742,9 @@ def import_experiment(archive, into) -> Path:
|
|
|
648
742
|
target.write_bytes(zipped.read(name))
|
|
649
743
|
flow_file = folder / flows[0]
|
|
650
744
|
data = json.loads(flow_file.read_text(encoding="utf-8"))
|
|
745
|
+
if flow_file.suffix.lower() in OLD_EXTENSIONS: # an experiment exported before 0.16
|
|
746
|
+
flow_file.unlink()
|
|
747
|
+
flow_file = analysis_name(flow_file)
|
|
651
748
|
moved: dict[tuple[str, str], str] = {}
|
|
652
749
|
for item in data.get("record", {}).get("inputs", []):
|
|
653
750
|
new = f"inputs/{Path(item['file']).name}"
|
|
@@ -697,6 +794,7 @@ def _as_file(value, stem: str):
|
|
|
697
794
|
#: (name, bytes or text) for a result worth a file of its own; the script an experiment carries uses it.
|
|
698
795
|
as_file = _as_file
|
|
699
796
|
|
|
700
|
-
__all__ = ["FORMAT", "
|
|
701
|
-
"
|
|
702
|
-
"
|
|
797
|
+
__all__ = ["EXTENSION", "FORMAT", "OLD_EXTENSIONS", "Record", "analysis_name", "as_file", "carry_over", "check",
|
|
798
|
+
"differences", "environment", "export_experiment", "fingerprint", "import_experiment",
|
|
799
|
+
"is_analysis_file", "is_experiment", "load", "load_all", "make_record", "requirements_lock", "save",
|
|
800
|
+
"sha256_file", "sha256_text", "versions"]
|
|
@@ -9,7 +9,7 @@ external assets, readable in any browser and printable.
|
|
|
9
9
|
|
|
10
10
|
The page is built from the workflow and its run alone; drawings (SVG) are
|
|
11
11
|
passed in by the app, which has the canvas to render them. So the same
|
|
12
|
-
report can be made without the app (``openprocess run analysis
|
|
12
|
+
report can be made without the app (``openprocess run orders.analysis
|
|
13
13
|
--report report.html``), minus the pictures.
|
|
14
14
|
"""
|
|
15
15
|
|