dataeval-flow 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dataeval_flow/__init__.py +93 -0
- dataeval_flow/__main__.py +149 -0
- dataeval_flow/_app/__init__.py +5 -0
- dataeval_flow/_app/_model/__init__.py +5 -0
- dataeval_flow/_app/_model/_coerce.py +126 -0
- dataeval_flow/_app/_model/_discover.py +171 -0
- dataeval_flow/_app/_model/_execution.py +108 -0
- dataeval_flow/_app/_model/_introspect.py +280 -0
- dataeval_flow/_app/_model/_item.py +213 -0
- dataeval_flow/_app/_model/_registry.py +255 -0
- dataeval_flow/_app/_model/_state.py +322 -0
- dataeval_flow/_app/_model/_undo.py +61 -0
- dataeval_flow/_app/_panes/__init__.py +35 -0
- dataeval_flow/_app/_panes/_config_pane.py +173 -0
- dataeval_flow/_app/_panes/_result_pane.py +125 -0
- dataeval_flow/_app/_panes/_task_pane.py +91 -0
- dataeval_flow/_app/_panes/_widgets.py +111 -0
- dataeval_flow/_app/_screens/__init__.py +25 -0
- dataeval_flow/_app/_screens/_base.py +242 -0
- dataeval_flow/_app/_screens/_detail.py +333 -0
- dataeval_flow/_app/_screens/_model.py +102 -0
- dataeval_flow/_app/_screens/_params.py +80 -0
- dataeval_flow/_app/_screens/_pathpicker.py +68 -0
- dataeval_flow/_app/_screens/_section.py +621 -0
- dataeval_flow/_app/_screens/_settings.py +183 -0
- dataeval_flow/_app/_viewmodel/__init__.py +15 -0
- dataeval_flow/_app/_viewmodel/_builder_vm.py +272 -0
- dataeval_flow/_app/_viewmodel/_model_vm.py +70 -0
- dataeval_flow/_app/_viewmodel/_rendering.py +189 -0
- dataeval_flow/_app/_viewmodel/_result_vm.py +210 -0
- dataeval_flow/_app/_viewmodel/_section_vm.py +224 -0
- dataeval_flow/_app/app.py +742 -0
- dataeval_flow/_app/cli.py +592 -0
- dataeval_flow/_logging.py +102 -0
- dataeval_flow/cache.py +1355 -0
- dataeval_flow/config/__init__.py +80 -0
- dataeval_flow/config/_loader.py +79 -0
- dataeval_flow/config/_merge.py +92 -0
- dataeval_flow/config/_models.py +115 -0
- dataeval_flow/config/_paths.py +85 -0
- dataeval_flow/config/schemas/__init__.py +112 -0
- dataeval_flow/config/schemas/_dataset.py +111 -0
- dataeval_flow/config/schemas/_extractor.py +119 -0
- dataeval_flow/config/schemas/_metadata.py +28 -0
- dataeval_flow/config/schemas/_preprocessor.py +18 -0
- dataeval_flow/config/schemas/_selection.py +100 -0
- dataeval_flow/config/schemas/_task.py +89 -0
- dataeval_flow/config/schemas/_workflow.py +135 -0
- dataeval_flow/dataset.py +635 -0
- dataeval_flow/embeddings.py +135 -0
- dataeval_flow/metadata.py +48 -0
- dataeval_flow/preprocessing.py +141 -0
- dataeval_flow/py.typed +0 -0
- dataeval_flow/runner.py +118 -0
- dataeval_flow/selection.py +50 -0
- dataeval_flow/workflow/__init__.py +328 -0
- dataeval_flow/workflow/_text_report.py +511 -0
- dataeval_flow/workflow/base.py +69 -0
- dataeval_flow/workflow/orchestrator.py +454 -0
- dataeval_flow/workflows/__init__.py +1 -0
- dataeval_flow/workflows/analysis/__init__.py +38 -0
- dataeval_flow/workflows/analysis/outputs.py +202 -0
- dataeval_flow/workflows/analysis/params.py +114 -0
- dataeval_flow/workflows/analysis/workflow.py +1313 -0
- dataeval_flow/workflows/cleaning/__init__.py +23 -0
- dataeval_flow/workflows/cleaning/outputs.py +200 -0
- dataeval_flow/workflows/cleaning/params.py +160 -0
- dataeval_flow/workflows/cleaning/report.py +304 -0
- dataeval_flow/workflows/cleaning/workflow.py +794 -0
- dataeval_flow/workflows/drift/__init__.py +1 -0
- dataeval_flow/workflows/drift/outputs.py +144 -0
- dataeval_flow/workflows/drift/params.py +332 -0
- dataeval_flow/workflows/drift/report.py +201 -0
- dataeval_flow/workflows/drift/workflow.py +647 -0
- dataeval_flow/workflows/ood/__init__.py +1 -0
- dataeval_flow/workflows/ood/outputs.py +134 -0
- dataeval_flow/workflows/ood/params.py +161 -0
- dataeval_flow/workflows/ood/report.py +311 -0
- dataeval_flow/workflows/ood/workflow.py +728 -0
- dataeval_flow/workflows/prioritization/__init__.py +1 -0
- dataeval_flow/workflows/prioritization/outputs.py +122 -0
- dataeval_flow/workflows/prioritization/params.py +124 -0
- dataeval_flow/workflows/prioritization/report.py +117 -0
- dataeval_flow/workflows/prioritization/workflow.py +587 -0
- dataeval_flow/workflows/splitting/__init__.py +25 -0
- dataeval_flow/workflows/splitting/outputs.py +101 -0
- dataeval_flow/workflows/splitting/params.py +61 -0
- dataeval_flow/workflows/splitting/report.py +485 -0
- dataeval_flow/workflows/splitting/workflow.py +371 -0
- dataeval_flow-0.1.0.dist-info/METADATA +305 -0
- dataeval_flow-0.1.0.dist-info/RECORD +94 -0
- dataeval_flow-0.1.0.dist-info/WHEEL +4 -0
- dataeval_flow-0.1.0.dist-info/entry_points.txt +2 -0
- dataeval_flow-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Param form helpers for step-builder modals (transforms/selections)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from textual.containers import Vertical
|
|
9
|
+
from textual.css.query import NoMatches
|
|
10
|
+
from textual.widgets import Checkbox, Input, Label, Select
|
|
11
|
+
|
|
12
|
+
from dataeval_flow._app._model._item import coerce_step_params, validate_step_params
|
|
13
|
+
from dataeval_flow._app._screens._base import _select_value
|
|
14
|
+
|
|
15
|
+
_log = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def build_param_form(container: Vertical, params: list[Any], prefix: str) -> None:
|
|
19
|
+
"""Populate *container* with widgets for each parameter."""
|
|
20
|
+
|
|
21
|
+
container.remove_children()
|
|
22
|
+
if not params:
|
|
23
|
+
return
|
|
24
|
+
for p in params:
|
|
25
|
+
suffix = "" if p.required else " (optional)"
|
|
26
|
+
label_text = f"{p.name} [{p.type_hint}]{suffix}:"
|
|
27
|
+
widget_id = f"{prefix}-{p.name}"
|
|
28
|
+
|
|
29
|
+
if p.choices:
|
|
30
|
+
container.mount(Label(label_text))
|
|
31
|
+
options = [(c, c) for c in p.choices]
|
|
32
|
+
has_default = p.default is not None and str(p.default) in p.choices
|
|
33
|
+
container.mount(
|
|
34
|
+
Select(
|
|
35
|
+
options,
|
|
36
|
+
id=widget_id,
|
|
37
|
+
allow_blank=not p.required,
|
|
38
|
+
**({"value": str(p.default)} if has_default else {}), # type: ignore
|
|
39
|
+
**({"prompt": "(default)"} if not p.required and not has_default else {}),
|
|
40
|
+
)
|
|
41
|
+
)
|
|
42
|
+
elif p.type_hint == "bool":
|
|
43
|
+
default_val = p.default if isinstance(p.default, bool) else False
|
|
44
|
+
container.mount(Checkbox(label_text, value=default_val, id=widget_id))
|
|
45
|
+
else:
|
|
46
|
+
container.mount(Label(label_text))
|
|
47
|
+
placeholder = str(p.default) if p.default is not None else ""
|
|
48
|
+
container.mount(Input(placeholder=placeholder, id=widget_id))
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _read_param_values(container: Vertical, params: list[Any], prefix: str) -> dict[str, str | bool | None]:
|
|
52
|
+
"""Read raw values from param form widgets into a plain dict."""
|
|
53
|
+
values: dict[str, str | bool | None] = {}
|
|
54
|
+
for p in params:
|
|
55
|
+
widget_id = f"{prefix}-{p.name}"
|
|
56
|
+
try:
|
|
57
|
+
if p.choices:
|
|
58
|
+
sel = container.query_one(f"#{widget_id}", Select)
|
|
59
|
+
values[p.name] = _select_value(sel) or None
|
|
60
|
+
elif p.type_hint == "bool":
|
|
61
|
+
cb = container.query_one(f"#{widget_id}", Checkbox)
|
|
62
|
+
values[p.name] = cb.value
|
|
63
|
+
else:
|
|
64
|
+
inp = container.query_one(f"#{widget_id}", Input)
|
|
65
|
+
values[p.name] = inp.value.strip() or None
|
|
66
|
+
except NoMatches:
|
|
67
|
+
_log.warning("Widget %s not found — skipping param '%s'", widget_id, p.name)
|
|
68
|
+
return values
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def validate_param_form(container: Vertical, params: list[Any], prefix: str) -> list[str]:
|
|
72
|
+
"""Validate the param form. Returns list of error messages."""
|
|
73
|
+
values = _read_param_values(container, params, prefix)
|
|
74
|
+
return validate_step_params(params, values)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def collect_param_form(container: Vertical, params: list[Any], prefix: str) -> dict[str, Any]:
|
|
78
|
+
"""Read values from a dynamic param form."""
|
|
79
|
+
values = _read_param_values(container, params, prefix)
|
|
80
|
+
return coerce_step_params(params, values)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# ---------------------------------------------------------------------------
|
|
2
|
+
# Path picker (file/folder selection modal)
|
|
3
|
+
# ---------------------------------------------------------------------------
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import contextlib
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from textual.app import ComposeResult, ScreenStackError
|
|
10
|
+
from textual.containers import Horizontal, Vertical
|
|
11
|
+
from textual.css.query import NoMatches
|
|
12
|
+
from textual.screen import ModalScreen
|
|
13
|
+
from textual.widgets import Button, DirectoryTree, Static
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class PathPickerScreen(ModalScreen[str | None]):
|
|
17
|
+
"""Unified file/folder picker.
|
|
18
|
+
|
|
19
|
+
- ``"file"``: selects files and directories (for loading configs).
|
|
20
|
+
- ``"folder"``: selects directories only (for browse buttons / save).
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
CSS = """
|
|
24
|
+
PathPickerScreen { align: center middle; }
|
|
25
|
+
#pp-dialog { width: 70; height: 30; border: round $accent 40%; background: $surface; padding: 1 2; }
|
|
26
|
+
#pp-tree { height: 1fr; margin: 1 0; }
|
|
27
|
+
#pp-selected { height: auto; margin: 0 0 1 0; }
|
|
28
|
+
#pp-buttons { height: auto; align: right middle; }
|
|
29
|
+
"""
|
|
30
|
+
BINDINGS = [("escape", "cancel", "Cancel")]
|
|
31
|
+
|
|
32
|
+
def __init__(self, start_path: str = ".", mode: str = "folder", **kwargs: Any) -> None:
|
|
33
|
+
super().__init__(**kwargs)
|
|
34
|
+
self._start_path = start_path
|
|
35
|
+
self._selected_path = start_path
|
|
36
|
+
self._mode = mode
|
|
37
|
+
|
|
38
|
+
def compose(self) -> ComposeResult:
|
|
39
|
+
title = "Select File" if self._mode == "file" else "Select Directory"
|
|
40
|
+
with Vertical(id="pp-dialog"):
|
|
41
|
+
yield Static(f"[bold]{title}[/bold]")
|
|
42
|
+
yield DirectoryTree(self._start_path, id="pp-tree")
|
|
43
|
+
yield Static(f"Selected: {self._start_path}", id="pp-selected")
|
|
44
|
+
with Horizontal(id="pp-buttons"):
|
|
45
|
+
yield Button("Select", id="btn-pp-select", variant="primary")
|
|
46
|
+
yield Button("Cancel", id="btn-pp-cancel")
|
|
47
|
+
|
|
48
|
+
def _update_selected(self, path: str) -> None:
|
|
49
|
+
self._selected_path = path
|
|
50
|
+
with contextlib.suppress(NoMatches):
|
|
51
|
+
self.query_one("#pp-selected", Static).update(f"Selected: {self._selected_path}")
|
|
52
|
+
|
|
53
|
+
def on_directory_tree_file_selected(self, event: DirectoryTree.FileSelected) -> None:
|
|
54
|
+
if self._mode == "file":
|
|
55
|
+
self._update_selected(str(event.path))
|
|
56
|
+
|
|
57
|
+
def on_directory_tree_directory_selected(self, event: DirectoryTree.DirectorySelected) -> None:
|
|
58
|
+
self._update_selected(str(event.path))
|
|
59
|
+
|
|
60
|
+
def on_button_pressed(self, event: Button.Pressed) -> None:
|
|
61
|
+
if event.button.id == "btn-pp-select":
|
|
62
|
+
self.dismiss(self._selected_path)
|
|
63
|
+
elif event.button.id == "btn-pp-cancel":
|
|
64
|
+
self.dismiss(None)
|
|
65
|
+
|
|
66
|
+
def action_cancel(self) -> None:
|
|
67
|
+
with contextlib.suppress(ScreenStackError):
|
|
68
|
+
self.dismiss(None)
|