truthound-dashboard 1.3.1__py3-none-any.whl → 1.4.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.
- truthound_dashboard/api/alerts.py +258 -0
- truthound_dashboard/api/anomaly.py +1302 -0
- truthound_dashboard/api/cross_alerts.py +352 -0
- truthound_dashboard/api/deps.py +143 -0
- truthound_dashboard/api/drift_monitor.py +540 -0
- truthound_dashboard/api/lineage.py +1151 -0
- truthound_dashboard/api/maintenance.py +363 -0
- truthound_dashboard/api/middleware.py +373 -1
- truthound_dashboard/api/model_monitoring.py +805 -0
- truthound_dashboard/api/notifications_advanced.py +2452 -0
- truthound_dashboard/api/plugins.py +2096 -0
- truthound_dashboard/api/profile.py +211 -14
- truthound_dashboard/api/reports.py +853 -0
- truthound_dashboard/api/router.py +147 -0
- truthound_dashboard/api/rule_suggestions.py +310 -0
- truthound_dashboard/api/schema_evolution.py +231 -0
- truthound_dashboard/api/sources.py +47 -3
- truthound_dashboard/api/triggers.py +190 -0
- truthound_dashboard/api/validations.py +13 -0
- truthound_dashboard/api/validators.py +333 -4
- truthound_dashboard/api/versioning.py +309 -0
- truthound_dashboard/api/websocket.py +301 -0
- truthound_dashboard/core/__init__.py +27 -0
- truthound_dashboard/core/anomaly.py +1395 -0
- truthound_dashboard/core/anomaly_explainer.py +633 -0
- truthound_dashboard/core/cache.py +206 -0
- truthound_dashboard/core/cached_services.py +422 -0
- truthound_dashboard/core/charts.py +352 -0
- truthound_dashboard/core/connections.py +1069 -42
- truthound_dashboard/core/cross_alerts.py +837 -0
- truthound_dashboard/core/drift_monitor.py +1477 -0
- truthound_dashboard/core/drift_sampling.py +669 -0
- truthound_dashboard/core/i18n/__init__.py +42 -0
- truthound_dashboard/core/i18n/detector.py +173 -0
- truthound_dashboard/core/i18n/messages.py +564 -0
- truthound_dashboard/core/lineage.py +971 -0
- truthound_dashboard/core/maintenance.py +443 -5
- truthound_dashboard/core/model_monitoring.py +1043 -0
- truthound_dashboard/core/notifications/channels.py +1020 -1
- truthound_dashboard/core/notifications/deduplication/__init__.py +143 -0
- truthound_dashboard/core/notifications/deduplication/policies.py +274 -0
- truthound_dashboard/core/notifications/deduplication/service.py +400 -0
- truthound_dashboard/core/notifications/deduplication/stores.py +2365 -0
- truthound_dashboard/core/notifications/deduplication/strategies.py +422 -0
- truthound_dashboard/core/notifications/dispatcher.py +43 -0
- truthound_dashboard/core/notifications/escalation/__init__.py +149 -0
- truthound_dashboard/core/notifications/escalation/backends.py +1384 -0
- truthound_dashboard/core/notifications/escalation/engine.py +429 -0
- truthound_dashboard/core/notifications/escalation/models.py +336 -0
- truthound_dashboard/core/notifications/escalation/scheduler.py +1187 -0
- truthound_dashboard/core/notifications/escalation/state_machine.py +330 -0
- truthound_dashboard/core/notifications/escalation/stores.py +2896 -0
- truthound_dashboard/core/notifications/events.py +49 -0
- truthound_dashboard/core/notifications/metrics/__init__.py +115 -0
- truthound_dashboard/core/notifications/metrics/base.py +528 -0
- truthound_dashboard/core/notifications/metrics/collectors.py +583 -0
- truthound_dashboard/core/notifications/routing/__init__.py +169 -0
- truthound_dashboard/core/notifications/routing/combinators.py +184 -0
- truthound_dashboard/core/notifications/routing/config.py +375 -0
- truthound_dashboard/core/notifications/routing/config_parser.py +867 -0
- truthound_dashboard/core/notifications/routing/engine.py +382 -0
- truthound_dashboard/core/notifications/routing/expression_engine.py +1269 -0
- truthound_dashboard/core/notifications/routing/jinja2_engine.py +774 -0
- truthound_dashboard/core/notifications/routing/rules.py +625 -0
- truthound_dashboard/core/notifications/routing/validator.py +678 -0
- truthound_dashboard/core/notifications/service.py +2 -0
- truthound_dashboard/core/notifications/stats_aggregator.py +850 -0
- truthound_dashboard/core/notifications/throttling/__init__.py +83 -0
- truthound_dashboard/core/notifications/throttling/builder.py +311 -0
- truthound_dashboard/core/notifications/throttling/stores.py +1859 -0
- truthound_dashboard/core/notifications/throttling/throttlers.py +633 -0
- truthound_dashboard/core/openlineage.py +1028 -0
- truthound_dashboard/core/plugins/__init__.py +39 -0
- truthound_dashboard/core/plugins/docs/__init__.py +39 -0
- truthound_dashboard/core/plugins/docs/extractor.py +703 -0
- truthound_dashboard/core/plugins/docs/renderers.py +804 -0
- truthound_dashboard/core/plugins/hooks/__init__.py +63 -0
- truthound_dashboard/core/plugins/hooks/decorators.py +367 -0
- truthound_dashboard/core/plugins/hooks/manager.py +403 -0
- truthound_dashboard/core/plugins/hooks/protocols.py +265 -0
- truthound_dashboard/core/plugins/lifecycle/__init__.py +41 -0
- truthound_dashboard/core/plugins/lifecycle/hot_reload.py +584 -0
- truthound_dashboard/core/plugins/lifecycle/machine.py +419 -0
- truthound_dashboard/core/plugins/lifecycle/states.py +266 -0
- truthound_dashboard/core/plugins/loader.py +504 -0
- truthound_dashboard/core/plugins/registry.py +810 -0
- truthound_dashboard/core/plugins/reporter_executor.py +588 -0
- truthound_dashboard/core/plugins/sandbox/__init__.py +59 -0
- truthound_dashboard/core/plugins/sandbox/code_validator.py +243 -0
- truthound_dashboard/core/plugins/sandbox/engines.py +770 -0
- truthound_dashboard/core/plugins/sandbox/protocols.py +194 -0
- truthound_dashboard/core/plugins/sandbox.py +617 -0
- truthound_dashboard/core/plugins/security/__init__.py +68 -0
- truthound_dashboard/core/plugins/security/analyzer.py +535 -0
- truthound_dashboard/core/plugins/security/policies.py +311 -0
- truthound_dashboard/core/plugins/security/protocols.py +296 -0
- truthound_dashboard/core/plugins/security/signing.py +842 -0
- truthound_dashboard/core/plugins/security.py +446 -0
- truthound_dashboard/core/plugins/validator_executor.py +401 -0
- truthound_dashboard/core/plugins/versioning/__init__.py +51 -0
- truthound_dashboard/core/plugins/versioning/constraints.py +377 -0
- truthound_dashboard/core/plugins/versioning/dependencies.py +541 -0
- truthound_dashboard/core/plugins/versioning/semver.py +266 -0
- truthound_dashboard/core/profile_comparison.py +601 -0
- truthound_dashboard/core/report_history.py +570 -0
- truthound_dashboard/core/reporters/__init__.py +57 -0
- truthound_dashboard/core/reporters/base.py +296 -0
- truthound_dashboard/core/reporters/csv_reporter.py +155 -0
- truthound_dashboard/core/reporters/html_reporter.py +598 -0
- truthound_dashboard/core/reporters/i18n/__init__.py +65 -0
- truthound_dashboard/core/reporters/i18n/base.py +494 -0
- truthound_dashboard/core/reporters/i18n/catalogs.py +930 -0
- truthound_dashboard/core/reporters/json_reporter.py +160 -0
- truthound_dashboard/core/reporters/junit_reporter.py +233 -0
- truthound_dashboard/core/reporters/markdown_reporter.py +207 -0
- truthound_dashboard/core/reporters/pdf_reporter.py +209 -0
- truthound_dashboard/core/reporters/registry.py +272 -0
- truthound_dashboard/core/rule_generator.py +2088 -0
- truthound_dashboard/core/scheduler.py +822 -12
- truthound_dashboard/core/schema_evolution.py +858 -0
- truthound_dashboard/core/services.py +152 -9
- truthound_dashboard/core/statistics.py +718 -0
- truthound_dashboard/core/streaming_anomaly.py +883 -0
- truthound_dashboard/core/triggers/__init__.py +45 -0
- truthound_dashboard/core/triggers/base.py +226 -0
- truthound_dashboard/core/triggers/evaluators.py +609 -0
- truthound_dashboard/core/triggers/factory.py +363 -0
- truthound_dashboard/core/unified_alerts.py +870 -0
- truthound_dashboard/core/validation_limits.py +509 -0
- truthound_dashboard/core/versioning.py +709 -0
- truthound_dashboard/core/websocket/__init__.py +59 -0
- truthound_dashboard/core/websocket/manager.py +512 -0
- truthound_dashboard/core/websocket/messages.py +130 -0
- truthound_dashboard/db/__init__.py +30 -0
- truthound_dashboard/db/models.py +3375 -3
- truthound_dashboard/main.py +22 -0
- truthound_dashboard/schemas/__init__.py +396 -1
- truthound_dashboard/schemas/anomaly.py +1258 -0
- truthound_dashboard/schemas/base.py +4 -0
- truthound_dashboard/schemas/cross_alerts.py +334 -0
- truthound_dashboard/schemas/drift_monitor.py +890 -0
- truthound_dashboard/schemas/lineage.py +428 -0
- truthound_dashboard/schemas/maintenance.py +154 -0
- truthound_dashboard/schemas/model_monitoring.py +374 -0
- truthound_dashboard/schemas/notifications_advanced.py +1363 -0
- truthound_dashboard/schemas/openlineage.py +704 -0
- truthound_dashboard/schemas/plugins.py +1293 -0
- truthound_dashboard/schemas/profile.py +420 -34
- truthound_dashboard/schemas/profile_comparison.py +242 -0
- truthound_dashboard/schemas/reports.py +285 -0
- truthound_dashboard/schemas/rule_suggestion.py +434 -0
- truthound_dashboard/schemas/schema_evolution.py +164 -0
- truthound_dashboard/schemas/source.py +117 -2
- truthound_dashboard/schemas/triggers.py +511 -0
- truthound_dashboard/schemas/unified_alerts.py +223 -0
- truthound_dashboard/schemas/validation.py +25 -1
- truthound_dashboard/schemas/validators/__init__.py +11 -0
- truthound_dashboard/schemas/validators/base.py +151 -0
- truthound_dashboard/schemas/versioning.py +152 -0
- truthound_dashboard/static/index.html +2 -2
- {truthound_dashboard-1.3.1.dist-info → truthound_dashboard-1.4.0.dist-info}/METADATA +142 -22
- truthound_dashboard-1.4.0.dist-info/RECORD +239 -0
- truthound_dashboard/static/assets/index-BZG20KuF.js +0 -586
- truthound_dashboard/static/assets/index-D_HyZ3pb.css +0 -1
- truthound_dashboard/static/assets/unmerged_dictionaries-CtpqQBm0.js +0 -1
- truthound_dashboard-1.3.1.dist-info/RECORD +0 -110
- {truthound_dashboard-1.3.1.dist-info → truthound_dashboard-1.4.0.dist-info}/WHEEL +0 -0
- {truthound_dashboard-1.3.1.dist-info → truthound_dashboard-1.4.0.dist-info}/entry_points.txt +0 -0
- {truthound_dashboard-1.3.1.dist-info → truthound_dashboard-1.4.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
"""Rule suggestion Pydantic schemas.
|
|
2
|
+
|
|
3
|
+
This module defines schemas for automatic rule generation
|
|
4
|
+
from profile data, including suggestions and application.
|
|
5
|
+
|
|
6
|
+
Features:
|
|
7
|
+
- Strictness levels (loose, medium, strict)
|
|
8
|
+
- Preset templates (default, comprehensive, minimal, etc.)
|
|
9
|
+
- Multiple export formats (yaml, json, python, toml)
|
|
10
|
+
- Category-based filtering
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from datetime import datetime
|
|
16
|
+
from enum import Enum
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
from pydantic import Field
|
|
20
|
+
|
|
21
|
+
from .base import BaseSchema
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# =============================================================================
|
|
25
|
+
# Enums for Rule Generation Options
|
|
26
|
+
# =============================================================================
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class StrictnessLevel(str, Enum):
|
|
30
|
+
"""Strictness level for rule generation.
|
|
31
|
+
|
|
32
|
+
Determines how permissive or strict generated rules will be.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
LOOSE = "loose" # Permissive thresholds, fewer rules
|
|
36
|
+
MEDIUM = "medium" # Balanced defaults (recommended)
|
|
37
|
+
STRICT = "strict" # Tight thresholds, comprehensive rules
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class RulePreset(str, Enum):
|
|
41
|
+
"""Preset templates for rule generation.
|
|
42
|
+
|
|
43
|
+
Pre-configured combinations of categories and settings.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
DEFAULT = "default" # General purpose
|
|
47
|
+
STRICT = "strict" # Production data
|
|
48
|
+
LOOSE = "loose" # Development/testing
|
|
49
|
+
MINIMAL = "minimal" # Essential rules only
|
|
50
|
+
COMPREHENSIVE = "comprehensive" # All available rules
|
|
51
|
+
CI_CD = "ci_cd" # Optimized for CI/CD pipelines
|
|
52
|
+
SCHEMA_ONLY = "schema_only" # Structure validation only
|
|
53
|
+
FORMAT_ONLY = "format_only" # Format/pattern rules only
|
|
54
|
+
CROSS_COLUMN = "cross_column" # Focus on cross-column relationships
|
|
55
|
+
DATA_INTEGRITY = "data_integrity" # Comprehensive data integrity
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class RuleExportFormat(str, Enum):
|
|
59
|
+
"""Export format for generated rules."""
|
|
60
|
+
|
|
61
|
+
YAML = "yaml" # Human-readable (default)
|
|
62
|
+
JSON = "json" # Machine-readable
|
|
63
|
+
PYTHON = "python" # Executable Python code
|
|
64
|
+
TOML = "toml" # Config-friendly
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class RuleCategory(str, Enum):
|
|
68
|
+
"""Categories of validation rules."""
|
|
69
|
+
|
|
70
|
+
SCHEMA = "schema" # Column existence, type constraints
|
|
71
|
+
STATISTICS = "stats" # Range, threshold rules
|
|
72
|
+
PATTERN = "pattern" # Regex, format rules
|
|
73
|
+
COMPLETENESS = "completeness" # Null ratio rules
|
|
74
|
+
UNIQUENESS = "uniqueness" # Primary key, unique constraints
|
|
75
|
+
DISTRIBUTION = "distribution" # Allowed values, cardinality
|
|
76
|
+
RELATIONSHIP = "relationship" # Cross-column relationships
|
|
77
|
+
MULTI_COLUMN = "multi_column" # Multi-column validations
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class CrossColumnRuleType(str, Enum):
|
|
81
|
+
"""Types of cross-column validation rules."""
|
|
82
|
+
|
|
83
|
+
# Composite Key Rules
|
|
84
|
+
COMPOSITE_KEY = "composite_key" # Multi-column uniqueness
|
|
85
|
+
|
|
86
|
+
# Arithmetic Relationships
|
|
87
|
+
COLUMN_SUM = "column_sum" # columns sum to target
|
|
88
|
+
COLUMN_PRODUCT = "column_product" # columns multiply to target
|
|
89
|
+
COLUMN_DIFFERENCE = "column_difference" # a - b = expected
|
|
90
|
+
COLUMN_RATIO = "column_ratio" # a / b = expected
|
|
91
|
+
COLUMN_PERCENTAGE = "column_percentage" # percentage relationship
|
|
92
|
+
|
|
93
|
+
# Comparison Relationships
|
|
94
|
+
COLUMN_COMPARISON = "column_comparison" # a > b, a < b, etc.
|
|
95
|
+
COLUMN_CHAIN_COMPARISON = "column_chain_comparison" # a < b < c
|
|
96
|
+
|
|
97
|
+
# Dependency Relationships
|
|
98
|
+
COLUMN_DEPENDENCY = "column_dependency" # functional dependency
|
|
99
|
+
COLUMN_IMPLICATION = "column_implication" # if A then B
|
|
100
|
+
COLUMN_COEXISTENCE = "column_coexistence" # all null or all non-null
|
|
101
|
+
COLUMN_MUTUAL_EXCLUSIVITY = "column_mutual_exclusivity" # at most one non-null
|
|
102
|
+
|
|
103
|
+
# Statistical Relationships
|
|
104
|
+
COLUMN_CORRELATION = "column_correlation" # correlation between columns
|
|
105
|
+
|
|
106
|
+
# Referential Integrity
|
|
107
|
+
REFERENTIAL_INTEGRITY = "referential_integrity" # foreign key validation
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
# =============================================================================
|
|
111
|
+
# Suggested Rule Schemas
|
|
112
|
+
# =============================================================================
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class SuggestedRule(BaseSchema):
|
|
116
|
+
"""A single suggested validation rule."""
|
|
117
|
+
|
|
118
|
+
column: str = Field(..., description="Target column name (or primary column for multi-column rules)")
|
|
119
|
+
validator_name: str = Field(..., description="Validator to apply")
|
|
120
|
+
params: dict[str, Any] = Field(
|
|
121
|
+
default_factory=dict, description="Validator parameters"
|
|
122
|
+
)
|
|
123
|
+
confidence: float = Field(
|
|
124
|
+
...,
|
|
125
|
+
ge=0.0,
|
|
126
|
+
le=1.0,
|
|
127
|
+
description="Confidence score (0.0 to 1.0)",
|
|
128
|
+
)
|
|
129
|
+
reason: str = Field(..., description="Why this rule is suggested")
|
|
130
|
+
severity_suggestion: str = Field(
|
|
131
|
+
default="medium",
|
|
132
|
+
description="Suggested severity level",
|
|
133
|
+
)
|
|
134
|
+
category: RuleCategory | str = Field(
|
|
135
|
+
default=RuleCategory.SCHEMA,
|
|
136
|
+
description="Rule category",
|
|
137
|
+
)
|
|
138
|
+
# Cross-column rule fields
|
|
139
|
+
is_cross_column: bool = Field(
|
|
140
|
+
default=False,
|
|
141
|
+
description="Whether this is a cross-column rule",
|
|
142
|
+
)
|
|
143
|
+
related_columns: list[str] = Field(
|
|
144
|
+
default_factory=list,
|
|
145
|
+
description="Additional columns involved in cross-column rules",
|
|
146
|
+
)
|
|
147
|
+
cross_column_type: CrossColumnRuleType | None = Field(
|
|
148
|
+
default=None,
|
|
149
|
+
description="Type of cross-column relationship (if applicable)",
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class CrossColumnRuleSuggestion(BaseSchema):
|
|
154
|
+
"""A suggested cross-column validation rule with detailed relationship info."""
|
|
155
|
+
|
|
156
|
+
id: str = Field(default_factory=lambda: "", description="Unique suggestion ID")
|
|
157
|
+
rule_type: CrossColumnRuleType = Field(..., description="Type of cross-column rule")
|
|
158
|
+
columns: list[str] = Field(..., description="Columns involved in the relationship")
|
|
159
|
+
validator_name: str = Field(..., description="Validator to apply")
|
|
160
|
+
params: dict[str, Any] = Field(
|
|
161
|
+
default_factory=dict, description="Validator parameters"
|
|
162
|
+
)
|
|
163
|
+
confidence: float = Field(
|
|
164
|
+
...,
|
|
165
|
+
ge=0.0,
|
|
166
|
+
le=1.0,
|
|
167
|
+
description="Confidence score (0.0 to 1.0)",
|
|
168
|
+
)
|
|
169
|
+
reason: str = Field(..., description="Why this rule is suggested")
|
|
170
|
+
severity_suggestion: str = Field(
|
|
171
|
+
default="medium",
|
|
172
|
+
description="Suggested severity level (high, medium, low)",
|
|
173
|
+
)
|
|
174
|
+
evidence: dict[str, Any] = Field(
|
|
175
|
+
default_factory=dict,
|
|
176
|
+
description="Statistical evidence supporting the suggestion",
|
|
177
|
+
)
|
|
178
|
+
sample_violations: list[dict[str, Any]] = Field(
|
|
179
|
+
default_factory=list,
|
|
180
|
+
description="Sample rows that would violate this rule",
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class RuleSuggestionRequest(BaseSchema):
|
|
185
|
+
"""Request to generate rule suggestions."""
|
|
186
|
+
|
|
187
|
+
use_latest_profile: bool = Field(
|
|
188
|
+
default=True,
|
|
189
|
+
description="Use the latest profile for suggestions",
|
|
190
|
+
)
|
|
191
|
+
profile_id: str | None = Field(
|
|
192
|
+
default=None,
|
|
193
|
+
description="Specific profile ID to use (if not latest)",
|
|
194
|
+
)
|
|
195
|
+
min_confidence: float = Field(
|
|
196
|
+
default=0.5,
|
|
197
|
+
ge=0.0,
|
|
198
|
+
le=1.0,
|
|
199
|
+
description="Minimum confidence threshold for suggestions",
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
# Advanced options
|
|
203
|
+
strictness: StrictnessLevel = Field(
|
|
204
|
+
default=StrictnessLevel.MEDIUM,
|
|
205
|
+
description="Strictness level for generated rules",
|
|
206
|
+
)
|
|
207
|
+
preset: RulePreset | None = Field(
|
|
208
|
+
default=None,
|
|
209
|
+
description="Preset template to use (overrides category settings)",
|
|
210
|
+
)
|
|
211
|
+
include_categories: list[RuleCategory] | None = Field(
|
|
212
|
+
default=None,
|
|
213
|
+
description="Only include rules from these categories",
|
|
214
|
+
)
|
|
215
|
+
exclude_categories: list[RuleCategory] | None = Field(
|
|
216
|
+
default=None,
|
|
217
|
+
description="Exclude rules from these categories",
|
|
218
|
+
)
|
|
219
|
+
include_types: list[str] | None = Field(
|
|
220
|
+
default=None,
|
|
221
|
+
description="Only suggest rules for these validator types",
|
|
222
|
+
)
|
|
223
|
+
exclude_columns: list[str] | None = Field(
|
|
224
|
+
default=None,
|
|
225
|
+
description="Columns to exclude from suggestions",
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# Cross-column rule options
|
|
229
|
+
enable_cross_column: bool = Field(
|
|
230
|
+
default=True,
|
|
231
|
+
description="Enable cross-column rule suggestions",
|
|
232
|
+
)
|
|
233
|
+
include_cross_column_types: list[CrossColumnRuleType] | None = Field(
|
|
234
|
+
default=None,
|
|
235
|
+
description="Only include these cross-column rule types",
|
|
236
|
+
)
|
|
237
|
+
exclude_cross_column_types: list[CrossColumnRuleType] | None = Field(
|
|
238
|
+
default=None,
|
|
239
|
+
description="Exclude these cross-column rule types",
|
|
240
|
+
)
|
|
241
|
+
sample_data_rows: int = Field(
|
|
242
|
+
default=1000,
|
|
243
|
+
ge=100,
|
|
244
|
+
le=10000,
|
|
245
|
+
description="Number of sample data rows to analyze for cross-column rules",
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
class RuleSuggestionResponse(BaseSchema):
|
|
250
|
+
"""Response containing suggested rules."""
|
|
251
|
+
|
|
252
|
+
source_id: str = Field(..., description="Source ID")
|
|
253
|
+
source_name: str = Field(..., description="Source name")
|
|
254
|
+
profile_id: str = Field(..., description="Profile ID used for suggestions")
|
|
255
|
+
suggestions: list[SuggestedRule] = Field(
|
|
256
|
+
default_factory=list, description="List of single-column suggested rules"
|
|
257
|
+
)
|
|
258
|
+
cross_column_suggestions: list[CrossColumnRuleSuggestion] = Field(
|
|
259
|
+
default_factory=list, description="List of cross-column suggested rules"
|
|
260
|
+
)
|
|
261
|
+
total_suggestions: int = Field(default=0, description="Total suggestions count (single + cross-column)")
|
|
262
|
+
high_confidence_count: int = Field(
|
|
263
|
+
default=0, description="Suggestions with confidence >= 0.8"
|
|
264
|
+
)
|
|
265
|
+
cross_column_count: int = Field(
|
|
266
|
+
default=0, description="Number of cross-column suggestions"
|
|
267
|
+
)
|
|
268
|
+
generated_at: datetime = Field(..., description="When suggestions were generated")
|
|
269
|
+
|
|
270
|
+
# Generation settings used
|
|
271
|
+
strictness: StrictnessLevel = Field(
|
|
272
|
+
default=StrictnessLevel.MEDIUM,
|
|
273
|
+
description="Strictness level used for generation",
|
|
274
|
+
)
|
|
275
|
+
preset: RulePreset | None = Field(
|
|
276
|
+
default=None,
|
|
277
|
+
description="Preset template used (if any)",
|
|
278
|
+
)
|
|
279
|
+
categories_included: list[RuleCategory] = Field(
|
|
280
|
+
default_factory=list,
|
|
281
|
+
description="Rule categories included in suggestions",
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
# Category breakdown
|
|
285
|
+
by_category: dict[str, int] = Field(
|
|
286
|
+
default_factory=dict,
|
|
287
|
+
description="Count of suggestions by category",
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
# Cross-column breakdown
|
|
291
|
+
by_cross_column_type: dict[str, int] = Field(
|
|
292
|
+
default_factory=dict,
|
|
293
|
+
description="Count of cross-column suggestions by type",
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
# =============================================================================
|
|
298
|
+
# Apply Rules Schemas
|
|
299
|
+
# =============================================================================
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
class ApplyRulesRequest(BaseSchema):
|
|
303
|
+
"""Request to apply selected rule suggestions."""
|
|
304
|
+
|
|
305
|
+
suggestions: list[SuggestedRule] = Field(
|
|
306
|
+
..., description="Selected suggestions to apply"
|
|
307
|
+
)
|
|
308
|
+
create_new_rule: bool = Field(
|
|
309
|
+
default=True,
|
|
310
|
+
description="Create a new rule set (vs updating existing)",
|
|
311
|
+
)
|
|
312
|
+
rule_name: str | None = Field(
|
|
313
|
+
default=None,
|
|
314
|
+
description="Name for the new rule set",
|
|
315
|
+
)
|
|
316
|
+
rule_description: str | None = Field(
|
|
317
|
+
default=None,
|
|
318
|
+
description="Description for the new rule set",
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
class ApplyRulesResponse(BaseSchema):
|
|
323
|
+
"""Response after applying rule suggestions."""
|
|
324
|
+
|
|
325
|
+
source_id: str = Field(..., description="Source ID")
|
|
326
|
+
rule_id: str = Field(..., description="Created/updated rule ID")
|
|
327
|
+
rule_name: str = Field(..., description="Rule name")
|
|
328
|
+
applied_count: int = Field(..., description="Number of rules applied")
|
|
329
|
+
validators: list[str] = Field(
|
|
330
|
+
default_factory=list, description="Applied validator names"
|
|
331
|
+
)
|
|
332
|
+
created_at: datetime = Field(..., description="When rule was created/updated")
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
# =============================================================================
|
|
336
|
+
# Suggestion Statistics
|
|
337
|
+
# =============================================================================
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
class SuggestionStats(BaseSchema):
|
|
341
|
+
"""Statistics about rule suggestions for a source."""
|
|
342
|
+
|
|
343
|
+
source_id: str = Field(..., description="Source ID")
|
|
344
|
+
last_suggestion_at: datetime | None = Field(
|
|
345
|
+
default=None, description="Last suggestion generation time"
|
|
346
|
+
)
|
|
347
|
+
total_suggestions_generated: int = Field(
|
|
348
|
+
default=0, description="Total suggestions ever generated"
|
|
349
|
+
)
|
|
350
|
+
total_suggestions_applied: int = Field(
|
|
351
|
+
default=0, description="Total suggestions applied"
|
|
352
|
+
)
|
|
353
|
+
suggestion_types: dict[str, int] = Field(
|
|
354
|
+
default_factory=dict,
|
|
355
|
+
description="Count of suggestions by validator type",
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
# =============================================================================
|
|
360
|
+
# Export Schemas
|
|
361
|
+
# =============================================================================
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
class ExportRulesRequest(BaseSchema):
|
|
365
|
+
"""Request to export generated rules in various formats."""
|
|
366
|
+
|
|
367
|
+
suggestions: list[SuggestedRule] = Field(
|
|
368
|
+
..., description="Rules to export"
|
|
369
|
+
)
|
|
370
|
+
format: RuleExportFormat = Field(
|
|
371
|
+
default=RuleExportFormat.YAML,
|
|
372
|
+
description="Export format",
|
|
373
|
+
)
|
|
374
|
+
include_metadata: bool = Field(
|
|
375
|
+
default=True,
|
|
376
|
+
description="Include generation metadata in export",
|
|
377
|
+
)
|
|
378
|
+
rule_name: str = Field(
|
|
379
|
+
default="auto_generated_rules",
|
|
380
|
+
description="Name for the exported rule set",
|
|
381
|
+
)
|
|
382
|
+
description: str | None = Field(
|
|
383
|
+
default=None,
|
|
384
|
+
description="Description for the rule set",
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
class ExportRulesResponse(BaseSchema):
|
|
389
|
+
"""Response containing exported rules content."""
|
|
390
|
+
|
|
391
|
+
content: str = Field(..., description="Exported content in requested format")
|
|
392
|
+
format: RuleExportFormat = Field(..., description="Export format used")
|
|
393
|
+
filename: str = Field(..., description="Suggested filename")
|
|
394
|
+
rule_count: int = Field(..., description="Number of rules exported")
|
|
395
|
+
generated_at: datetime = Field(..., description="When export was generated")
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
# =============================================================================
|
|
399
|
+
# Preset Configuration
|
|
400
|
+
# =============================================================================
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
class PresetInfo(BaseSchema):
|
|
404
|
+
"""Information about a rule generation preset."""
|
|
405
|
+
|
|
406
|
+
name: RulePreset = Field(..., description="Preset name")
|
|
407
|
+
display_name: str = Field(..., description="Human-readable name")
|
|
408
|
+
description: str = Field(..., description="What this preset does")
|
|
409
|
+
strictness: StrictnessLevel = Field(..., description="Default strictness")
|
|
410
|
+
categories: list[RuleCategory] = Field(
|
|
411
|
+
default_factory=list,
|
|
412
|
+
description="Categories included in this preset",
|
|
413
|
+
)
|
|
414
|
+
recommended_for: str = Field(
|
|
415
|
+
default="",
|
|
416
|
+
description="Use case this preset is recommended for",
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
class PresetsResponse(BaseSchema):
|
|
421
|
+
"""Response listing available presets."""
|
|
422
|
+
|
|
423
|
+
presets: list[PresetInfo] = Field(
|
|
424
|
+
default_factory=list, description="Available presets"
|
|
425
|
+
)
|
|
426
|
+
strictness_levels: list[str] = Field(
|
|
427
|
+
default_factory=list, description="Available strictness levels"
|
|
428
|
+
)
|
|
429
|
+
categories: list[str] = Field(
|
|
430
|
+
default_factory=list, description="Available categories"
|
|
431
|
+
)
|
|
432
|
+
export_formats: list[str] = Field(
|
|
433
|
+
default_factory=list, description="Available export formats"
|
|
434
|
+
)
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""Schema evolution Pydantic schemas.
|
|
2
|
+
|
|
3
|
+
This module defines schemas for schema evolution detection,
|
|
4
|
+
version tracking, and change notifications.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from enum import Enum
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from pydantic import Field
|
|
14
|
+
|
|
15
|
+
from .base import BaseSchema, IDMixin, TimestampMixin
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SchemaChangeType(str, Enum):
|
|
19
|
+
"""Type of schema change."""
|
|
20
|
+
|
|
21
|
+
COLUMN_ADDED = "column_added"
|
|
22
|
+
COLUMN_REMOVED = "column_removed"
|
|
23
|
+
TYPE_CHANGED = "type_changed"
|
|
24
|
+
NULLABLE_CHANGED = "nullable_changed"
|
|
25
|
+
CONSTRAINT_CHANGED = "constraint_changed"
|
|
26
|
+
COLUMN_RENAMED = "column_renamed"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class SchemaChangeSeverity(str, Enum):
|
|
30
|
+
"""Severity level of schema change."""
|
|
31
|
+
|
|
32
|
+
BREAKING = "breaking"
|
|
33
|
+
WARNING = "warning"
|
|
34
|
+
NON_BREAKING = "non_breaking"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# =============================================================================
|
|
38
|
+
# Schema Version Schemas
|
|
39
|
+
# =============================================================================
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class SchemaVersionBase(BaseSchema):
|
|
43
|
+
"""Base schema for schema version."""
|
|
44
|
+
|
|
45
|
+
version_number: int = Field(..., description="Sequential version number")
|
|
46
|
+
column_count: int = Field(..., description="Number of columns in this version")
|
|
47
|
+
columns: list[str] = Field(
|
|
48
|
+
default_factory=list, description="List of column names"
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class SchemaVersionResponse(SchemaVersionBase, IDMixin, TimestampMixin):
|
|
53
|
+
"""Schema version response."""
|
|
54
|
+
|
|
55
|
+
source_id: str = Field(..., description="Source ID")
|
|
56
|
+
schema_id: str = Field(..., description="Schema record ID")
|
|
57
|
+
schema_hash: str = Field(..., description="SHA256 hash of schema structure")
|
|
58
|
+
column_snapshot: dict[str, Any] = Field(
|
|
59
|
+
default_factory=dict, description="Full column definitions"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class SchemaVersionSummary(BaseSchema):
|
|
64
|
+
"""Summary of a schema version for lists."""
|
|
65
|
+
|
|
66
|
+
id: str = Field(..., description="Version ID")
|
|
67
|
+
version_number: int = Field(..., description="Version number")
|
|
68
|
+
column_count: int = Field(..., description="Number of columns")
|
|
69
|
+
created_at: datetime = Field(..., description="When version was created")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class SchemaVersionListResponse(BaseSchema):
|
|
73
|
+
"""List response for schema versions."""
|
|
74
|
+
|
|
75
|
+
versions: list[SchemaVersionSummary] = Field(
|
|
76
|
+
default_factory=list, description="List of schema versions"
|
|
77
|
+
)
|
|
78
|
+
total: int = Field(default=0, description="Total count")
|
|
79
|
+
source_id: str = Field(..., description="Source ID")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# =============================================================================
|
|
83
|
+
# Schema Change Schemas
|
|
84
|
+
# =============================================================================
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class SchemaChangeBase(BaseSchema):
|
|
88
|
+
"""Base schema for individual change."""
|
|
89
|
+
|
|
90
|
+
change_type: SchemaChangeType = Field(..., description="Type of change")
|
|
91
|
+
column_name: str = Field(..., description="Affected column name")
|
|
92
|
+
old_value: str | None = Field(default=None, description="Previous value")
|
|
93
|
+
new_value: str | None = Field(default=None, description="New value")
|
|
94
|
+
severity: SchemaChangeSeverity = Field(
|
|
95
|
+
default=SchemaChangeSeverity.NON_BREAKING,
|
|
96
|
+
description="Severity of the change",
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class SchemaChangeResponse(SchemaChangeBase, IDMixin):
|
|
101
|
+
"""Response schema for a single schema change."""
|
|
102
|
+
|
|
103
|
+
source_id: str = Field(..., description="Source ID")
|
|
104
|
+
from_version_id: str | None = Field(
|
|
105
|
+
default=None, description="Previous version ID"
|
|
106
|
+
)
|
|
107
|
+
to_version_id: str = Field(..., description="New version ID")
|
|
108
|
+
description: str = Field(..., description="Human-readable description")
|
|
109
|
+
created_at: datetime = Field(..., description="When change was detected")
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class SchemaChangeListResponse(BaseSchema):
|
|
113
|
+
"""List response for schema changes."""
|
|
114
|
+
|
|
115
|
+
changes: list[SchemaChangeResponse] = Field(
|
|
116
|
+
default_factory=list, description="List of changes"
|
|
117
|
+
)
|
|
118
|
+
total: int = Field(default=0, description="Total count")
|
|
119
|
+
source_id: str = Field(..., description="Source ID")
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# =============================================================================
|
|
123
|
+
# Schema Evolution Detection Schemas
|
|
124
|
+
# =============================================================================
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class SchemaEvolutionRequest(BaseSchema):
|
|
128
|
+
"""Request to detect schema changes."""
|
|
129
|
+
|
|
130
|
+
force_relearn: bool = Field(
|
|
131
|
+
default=False,
|
|
132
|
+
description="Force re-learning schema even if unchanged",
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class SchemaEvolutionResponse(BaseSchema):
|
|
137
|
+
"""Response for schema evolution detection."""
|
|
138
|
+
|
|
139
|
+
source_id: str = Field(..., description="Source ID")
|
|
140
|
+
source_name: str = Field(..., description="Source name")
|
|
141
|
+
from_version: int | None = Field(
|
|
142
|
+
default=None, description="Previous version number"
|
|
143
|
+
)
|
|
144
|
+
to_version: int = Field(..., description="New version number")
|
|
145
|
+
has_changes: bool = Field(..., description="Whether changes were detected")
|
|
146
|
+
total_changes: int = Field(default=0, description="Total number of changes")
|
|
147
|
+
breaking_changes: int = Field(default=0, description="Number of breaking changes")
|
|
148
|
+
changes: list[SchemaChangeResponse] = Field(
|
|
149
|
+
default_factory=list, description="List of detected changes"
|
|
150
|
+
)
|
|
151
|
+
detected_at: datetime = Field(..., description="When detection was performed")
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class SchemaEvolutionSummary(BaseSchema):
|
|
155
|
+
"""Summary of schema evolution for a source."""
|
|
156
|
+
|
|
157
|
+
source_id: str = Field(..., description="Source ID")
|
|
158
|
+
current_version: int = Field(..., description="Current version number")
|
|
159
|
+
total_versions: int = Field(..., description="Total number of versions")
|
|
160
|
+
total_changes: int = Field(..., description="Total changes across all versions")
|
|
161
|
+
breaking_changes: int = Field(..., description="Total breaking changes")
|
|
162
|
+
last_change_at: datetime | None = Field(
|
|
163
|
+
default=None, description="Last change timestamp"
|
|
164
|
+
)
|