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 @@
|
|
|
1
|
+
"""Data prioritization workflow."""
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""Data prioritization workflow outputs."""
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Literal, TypeAlias
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
from typing_extensions import TypedDict, TypeIs
|
|
7
|
+
|
|
8
|
+
from dataeval_flow.config import ResultMetadata
|
|
9
|
+
from dataeval_flow.workflow.base import Reportable, WorkflowOutputsBase, WorkflowReportBase
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from dataeval_flow.workflow import WorkflowResult
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"CleaningSummaryDict",
|
|
16
|
+
"DataPrioritizationMetadata",
|
|
17
|
+
"DataPrioritizationOutputs",
|
|
18
|
+
"DataPrioritizationRawOutputs",
|
|
19
|
+
"DataPrioritizationReport",
|
|
20
|
+
"DataPrioritizationResult",
|
|
21
|
+
"PerDatasetPrioritizationDict",
|
|
22
|
+
"is_prioritization_result",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# ---------------------------------------------------------------------------
|
|
27
|
+
# TypedDicts for serialized outputs
|
|
28
|
+
# ---------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class PerDatasetPrioritizationDict(TypedDict):
|
|
32
|
+
"""Prioritization results for a single additional dataset."""
|
|
33
|
+
|
|
34
|
+
source_name: str
|
|
35
|
+
original_size: int
|
|
36
|
+
cleaned_size: int
|
|
37
|
+
prioritized_indices: list[int]
|
|
38
|
+
scores: list[float] | None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class CleaningSummaryDict(TypedDict):
|
|
42
|
+
"""Summary of the cleaning step (combined across all datasets)."""
|
|
43
|
+
|
|
44
|
+
total_combined: int
|
|
45
|
+
outliers_flagged: int
|
|
46
|
+
duplicates_flagged: int
|
|
47
|
+
total_removed: int
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# ---------------------------------------------------------------------------
|
|
51
|
+
# Pydantic output models
|
|
52
|
+
# ---------------------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class DataPrioritizationRawOutputs(WorkflowOutputsBase):
|
|
56
|
+
"""Machine-readable results from the data-prioritization workflow."""
|
|
57
|
+
|
|
58
|
+
reference_size: int = Field(
|
|
59
|
+
default=0,
|
|
60
|
+
description="Number of items in the reference dataset.",
|
|
61
|
+
)
|
|
62
|
+
method: str = Field(
|
|
63
|
+
default="",
|
|
64
|
+
description="Ranking method used.",
|
|
65
|
+
)
|
|
66
|
+
order: str = Field(
|
|
67
|
+
default="",
|
|
68
|
+
description="Sort direction used.",
|
|
69
|
+
)
|
|
70
|
+
policy: str = Field(
|
|
71
|
+
default="",
|
|
72
|
+
description="Selection policy used.",
|
|
73
|
+
)
|
|
74
|
+
cleaning_summary: CleaningSummaryDict | None = Field(
|
|
75
|
+
default=None,
|
|
76
|
+
description="Cleaning step summary. None if cleaning was skipped.",
|
|
77
|
+
)
|
|
78
|
+
prioritizations: list[PerDatasetPrioritizationDict] = Field(
|
|
79
|
+
default_factory=list,
|
|
80
|
+
description="Per-dataset prioritization results.",
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class DataPrioritizationReport(WorkflowReportBase):
|
|
85
|
+
"""Human-readable report for the data-prioritization workflow."""
|
|
86
|
+
|
|
87
|
+
findings: list[Reportable] = Field(default_factory=list)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class DataPrioritizationOutputs(BaseModel):
|
|
91
|
+
"""Complete data-prioritization workflow output."""
|
|
92
|
+
|
|
93
|
+
raw: DataPrioritizationRawOutputs
|
|
94
|
+
report: DataPrioritizationReport
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class DataPrioritizationMetadata(ResultMetadata):
|
|
98
|
+
"""Metadata for the data-prioritization workflow."""
|
|
99
|
+
|
|
100
|
+
mode: Literal["advisory", "preparatory"] = "advisory"
|
|
101
|
+
method: str = ""
|
|
102
|
+
order: str = ""
|
|
103
|
+
policy: str = ""
|
|
104
|
+
cleaning_enabled: bool = False
|
|
105
|
+
items_removed_by_cleaning: int = 0
|
|
106
|
+
per_source_clean_indices: dict[str, list[int]] = Field(default_factory=dict)
|
|
107
|
+
per_source_prioritized_indices: dict[str, list[int]] = Field(default_factory=dict)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
# ---------------------------------------------------------------------------
|
|
111
|
+
# Type alias and TypeIs guard for type narrowing
|
|
112
|
+
# ---------------------------------------------------------------------------
|
|
113
|
+
|
|
114
|
+
#: Fully typed result alias for the data-prioritization workflow.
|
|
115
|
+
DataPrioritizationResult: TypeAlias = "WorkflowResult[DataPrioritizationMetadata, DataPrioritizationOutputs]"
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def is_prioritization_result(
|
|
119
|
+
result: "WorkflowResult[Any, Any]",
|
|
120
|
+
) -> TypeIs["WorkflowResult[DataPrioritizationMetadata, DataPrioritizationOutputs]"]:
|
|
121
|
+
"""Narrow a generic ``WorkflowResult`` to a data-prioritization result."""
|
|
122
|
+
return isinstance(result.metadata, DataPrioritizationMetadata)
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""Data prioritization workflow parameters."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Sequence
|
|
4
|
+
from typing import ClassVar, Literal
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
7
|
+
|
|
8
|
+
from dataeval_flow.workflow.base import WorkflowParametersBase
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"CleaningConfig",
|
|
12
|
+
"DataPrioritizationHealthThresholds",
|
|
13
|
+
"DataPrioritizationParameters",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
MethodType = Literal["knn", "kmeans_distance", "kmeans_complexity", "hdbscan_distance", "hdbscan_complexity"]
|
|
17
|
+
OrderType = Literal["easy_first", "hard_first"]
|
|
18
|
+
PolicyType = Literal["difficulty", "stratified", "class_balanced"]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class CleaningConfig(BaseModel):
|
|
22
|
+
"""Optional cleaning sub-config for outlier/duplicate removal before prioritization.
|
|
23
|
+
|
|
24
|
+
When provided, outlier and duplicate detection runs across all datasets
|
|
25
|
+
before prioritization. Flagged items are excluded from the ranking.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
model_config: ClassVar[ConfigDict] = ConfigDict(extra="forbid")
|
|
29
|
+
|
|
30
|
+
outlier_method: Literal["adaptive", "zscore", "modzscore", "iqr"] = Field(
|
|
31
|
+
description="Statistical method for outlier detection.",
|
|
32
|
+
)
|
|
33
|
+
outlier_flags: Sequence[Literal["dimension", "pixel", "visual"]] = Field(
|
|
34
|
+
min_length=1,
|
|
35
|
+
description="Image statistics groups for outlier detection. At least one required.",
|
|
36
|
+
)
|
|
37
|
+
outlier_threshold: float | None = Field(
|
|
38
|
+
default=None,
|
|
39
|
+
ge=0.0,
|
|
40
|
+
description="Custom threshold (None = use DataEval default for chosen method).",
|
|
41
|
+
)
|
|
42
|
+
duplicate_flags: Sequence[Literal["hash_basic", "hash_d4"]] | None = Field(
|
|
43
|
+
default=None,
|
|
44
|
+
description="Hash flag groups for duplicate detection. None = DataEval default (hash_basic).",
|
|
45
|
+
)
|
|
46
|
+
duplicate_merge_near: bool = Field(
|
|
47
|
+
default=True,
|
|
48
|
+
description="Merge overlapping near-duplicate groups from different detection methods.",
|
|
49
|
+
)
|
|
50
|
+
duplicate_exact_only: bool = Field(
|
|
51
|
+
default=False,
|
|
52
|
+
description="When True, only flag exact duplicates — skip near-duplicate detection.",
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class DataPrioritizationHealthThresholds(BaseModel):
|
|
57
|
+
"""Thresholds that control finding severity for the prioritization report."""
|
|
58
|
+
|
|
59
|
+
cleaning_removed_pct_warning: float = Field(
|
|
60
|
+
default=20.0,
|
|
61
|
+
ge=0.0,
|
|
62
|
+
le=100.0,
|
|
63
|
+
description="Warn if cleaning removes more than this percentage of the combined dataset.",
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class DataPrioritizationParameters(WorkflowParametersBase):
|
|
68
|
+
"""Parameters for the data-prioritization workflow.
|
|
69
|
+
|
|
70
|
+
Requires at least two sources: the first is the reference (labeled) dataset,
|
|
71
|
+
and subsequent sources are unlabeled data pools to prioritize.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
# --- Prioritization method ---
|
|
75
|
+
method: MethodType = Field(
|
|
76
|
+
default="knn",
|
|
77
|
+
description="Ranking method: knn, kmeans_distance, kmeans_complexity, hdbscan_distance, hdbscan_complexity.",
|
|
78
|
+
)
|
|
79
|
+
k: int | None = Field(
|
|
80
|
+
default=None,
|
|
81
|
+
gt=0,
|
|
82
|
+
description="Number of nearest neighbors for knn method. None = sqrt(n_samples).",
|
|
83
|
+
)
|
|
84
|
+
c: int | None = Field(
|
|
85
|
+
default=None,
|
|
86
|
+
gt=0,
|
|
87
|
+
description="Number of clusters for clustering methods. None = sqrt(n_samples).",
|
|
88
|
+
)
|
|
89
|
+
n_init: int | Literal["auto"] = Field(
|
|
90
|
+
default="auto",
|
|
91
|
+
description="Number of K-means initializations (kmeans methods only).",
|
|
92
|
+
)
|
|
93
|
+
max_cluster_size: int | None = Field(
|
|
94
|
+
default=None,
|
|
95
|
+
gt=0,
|
|
96
|
+
description="Maximum cluster size for HDBSCAN methods.",
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# --- Order and policy ---
|
|
100
|
+
order: OrderType = Field(
|
|
101
|
+
default="hard_first",
|
|
102
|
+
description="Sort direction: easy_first (prototypical first) or hard_first (novel/challenging first).",
|
|
103
|
+
)
|
|
104
|
+
policy: PolicyType = Field(
|
|
105
|
+
default="difficulty",
|
|
106
|
+
description="Selection policy: difficulty (direct ordering), stratified (binned), class_balanced.",
|
|
107
|
+
)
|
|
108
|
+
num_bins: int = Field(
|
|
109
|
+
default=50,
|
|
110
|
+
gt=0,
|
|
111
|
+
description="Number of bins for stratified policy.",
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# --- Optional cleaning ---
|
|
115
|
+
cleaning: CleaningConfig | None = Field(
|
|
116
|
+
default=None,
|
|
117
|
+
description="Optional cleaning config. When set, outlier/duplicate detection runs before prioritization.",
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
# --- Health thresholds ---
|
|
121
|
+
health_thresholds: DataPrioritizationHealthThresholds = Field(
|
|
122
|
+
default_factory=DataPrioritizationHealthThresholds,
|
|
123
|
+
description="Warning thresholds for the prioritization report.",
|
|
124
|
+
)
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""Findings builders for the data-prioritization workflow."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Literal
|
|
6
|
+
|
|
7
|
+
from dataeval_flow.workflow.base import Reportable
|
|
8
|
+
from dataeval_flow.workflows.prioritization.outputs import (
|
|
9
|
+
CleaningSummaryDict,
|
|
10
|
+
DataPrioritizationRawOutputs,
|
|
11
|
+
PerDatasetPrioritizationDict,
|
|
12
|
+
)
|
|
13
|
+
from dataeval_flow.workflows.prioritization.params import (
|
|
14
|
+
DataPrioritizationHealthThresholds,
|
|
15
|
+
DataPrioritizationParameters,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _severity_for_cleaning(
|
|
20
|
+
removed_pct: float,
|
|
21
|
+
thresholds: DataPrioritizationHealthThresholds,
|
|
22
|
+
) -> Literal["ok", "info", "warning"]:
|
|
23
|
+
"""Determine severity based on percentage of items removed by cleaning."""
|
|
24
|
+
if removed_pct >= thresholds.cleaning_removed_pct_warning:
|
|
25
|
+
return "warning"
|
|
26
|
+
if removed_pct > 0:
|
|
27
|
+
return "info"
|
|
28
|
+
return "ok"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _build_cleaning_finding(
|
|
32
|
+
summary: CleaningSummaryDict,
|
|
33
|
+
thresholds: DataPrioritizationHealthThresholds,
|
|
34
|
+
) -> Reportable:
|
|
35
|
+
"""Build a finding for the cleaning step."""
|
|
36
|
+
total = summary["total_combined"]
|
|
37
|
+
removed = summary["total_removed"]
|
|
38
|
+
removed_pct = (removed / total * 100) if total > 0 else 0.0
|
|
39
|
+
severity = _severity_for_cleaning(removed_pct, thresholds)
|
|
40
|
+
|
|
41
|
+
data: dict[str, Any] = {
|
|
42
|
+
"brief": f"{removed} items ({removed_pct:.1f}%)",
|
|
43
|
+
"total_combined": total,
|
|
44
|
+
"outliers_flagged": summary["outliers_flagged"],
|
|
45
|
+
"duplicates_flagged": summary["duplicates_flagged"],
|
|
46
|
+
"total_removed": removed,
|
|
47
|
+
"removed_pct": f"{removed_pct:.1f}%",
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
description = (
|
|
51
|
+
f"Pruning removed {removed}/{total} items ({removed_pct:.1f}%): "
|
|
52
|
+
f"{summary['outliers_flagged']} outliers, {summary['duplicates_flagged']} duplicates"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
return Reportable(
|
|
56
|
+
report_type="key_value",
|
|
57
|
+
severity=severity,
|
|
58
|
+
title="Pruning",
|
|
59
|
+
data=data,
|
|
60
|
+
description=description,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _build_prioritization_finding(
|
|
65
|
+
result: PerDatasetPrioritizationDict,
|
|
66
|
+
method: str,
|
|
67
|
+
order: str,
|
|
68
|
+
policy: str,
|
|
69
|
+
) -> Reportable:
|
|
70
|
+
"""Build a finding for a single dataset's prioritization results."""
|
|
71
|
+
n_items = result["cleaned_size"]
|
|
72
|
+
top_n = min(10, len(result["prioritized_indices"]))
|
|
73
|
+
top_indices = result["prioritized_indices"][:top_n]
|
|
74
|
+
|
|
75
|
+
data: dict[str, Any] = {
|
|
76
|
+
"brief": f"{n_items} items",
|
|
77
|
+
"source": result["source_name"],
|
|
78
|
+
"original_size": result["original_size"],
|
|
79
|
+
"cleaned_size": result["cleaned_size"],
|
|
80
|
+
"method": method,
|
|
81
|
+
"order": order,
|
|
82
|
+
"policy": policy,
|
|
83
|
+
"top_indices": top_indices,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if result["scores"] is not None:
|
|
87
|
+
top_scores = result["scores"][:top_n]
|
|
88
|
+
data["top_scores"] = [round(s, 4) for s in top_scores]
|
|
89
|
+
|
|
90
|
+
description = f"{result['source_name']}: {n_items} items prioritized via {method} ({order}, {policy})"
|
|
91
|
+
|
|
92
|
+
return Reportable(
|
|
93
|
+
report_type="key_value",
|
|
94
|
+
severity="info",
|
|
95
|
+
title=f"Prioritization: {result['source_name']}",
|
|
96
|
+
data=data,
|
|
97
|
+
description=description,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def build_findings(
|
|
102
|
+
raw: DataPrioritizationRawOutputs,
|
|
103
|
+
params: DataPrioritizationParameters,
|
|
104
|
+
) -> list[Reportable]:
|
|
105
|
+
"""Build all report findings from raw results."""
|
|
106
|
+
findings: list[Reportable] = []
|
|
107
|
+
|
|
108
|
+
# Cleaning finding
|
|
109
|
+
if raw.cleaning_summary is not None:
|
|
110
|
+
findings.append(_build_cleaning_finding(raw.cleaning_summary, params.health_thresholds))
|
|
111
|
+
|
|
112
|
+
# Per-dataset prioritization findings
|
|
113
|
+
findings.extend(
|
|
114
|
+
_build_prioritization_finding(result, raw.method, raw.order, raw.policy) for result in raw.prioritizations
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
return findings
|