truthound-dashboard 1.4.3__py3-none-any.whl → 1.5.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.
Files changed (205) hide show
  1. truthound_dashboard/api/alerts.py +75 -86
  2. truthound_dashboard/api/anomaly.py +7 -13
  3. truthound_dashboard/api/cross_alerts.py +38 -52
  4. truthound_dashboard/api/drift.py +49 -59
  5. truthound_dashboard/api/drift_monitor.py +234 -79
  6. truthound_dashboard/api/enterprise_sampling.py +498 -0
  7. truthound_dashboard/api/history.py +57 -5
  8. truthound_dashboard/api/lineage.py +3 -48
  9. truthound_dashboard/api/maintenance.py +104 -49
  10. truthound_dashboard/api/mask.py +1 -2
  11. truthound_dashboard/api/middleware.py +2 -1
  12. truthound_dashboard/api/model_monitoring.py +435 -311
  13. truthound_dashboard/api/notifications.py +227 -191
  14. truthound_dashboard/api/notifications_advanced.py +21 -20
  15. truthound_dashboard/api/observability.py +586 -0
  16. truthound_dashboard/api/plugins.py +2 -433
  17. truthound_dashboard/api/profile.py +199 -37
  18. truthound_dashboard/api/quality_reporter.py +701 -0
  19. truthound_dashboard/api/reports.py +7 -16
  20. truthound_dashboard/api/router.py +66 -0
  21. truthound_dashboard/api/rule_suggestions.py +5 -5
  22. truthound_dashboard/api/scan.py +17 -19
  23. truthound_dashboard/api/schedules.py +85 -50
  24. truthound_dashboard/api/schema_evolution.py +6 -6
  25. truthound_dashboard/api/schema_watcher.py +667 -0
  26. truthound_dashboard/api/sources.py +98 -27
  27. truthound_dashboard/api/tiering.py +1323 -0
  28. truthound_dashboard/api/triggers.py +14 -11
  29. truthound_dashboard/api/validations.py +12 -11
  30. truthound_dashboard/api/versioning.py +1 -6
  31. truthound_dashboard/core/__init__.py +129 -3
  32. truthound_dashboard/core/actions/__init__.py +62 -0
  33. truthound_dashboard/core/actions/custom.py +426 -0
  34. truthound_dashboard/core/actions/notifications.py +910 -0
  35. truthound_dashboard/core/actions/storage.py +472 -0
  36. truthound_dashboard/core/actions/webhook.py +281 -0
  37. truthound_dashboard/core/anomaly.py +262 -67
  38. truthound_dashboard/core/anomaly_explainer.py +4 -3
  39. truthound_dashboard/core/backends/__init__.py +67 -0
  40. truthound_dashboard/core/backends/base.py +299 -0
  41. truthound_dashboard/core/backends/errors.py +191 -0
  42. truthound_dashboard/core/backends/factory.py +423 -0
  43. truthound_dashboard/core/backends/mock_backend.py +451 -0
  44. truthound_dashboard/core/backends/truthound_backend.py +718 -0
  45. truthound_dashboard/core/checkpoint/__init__.py +87 -0
  46. truthound_dashboard/core/checkpoint/adapters.py +814 -0
  47. truthound_dashboard/core/checkpoint/checkpoint.py +491 -0
  48. truthound_dashboard/core/checkpoint/runner.py +270 -0
  49. truthound_dashboard/core/connections.py +437 -10
  50. truthound_dashboard/core/converters/__init__.py +14 -0
  51. truthound_dashboard/core/converters/truthound.py +620 -0
  52. truthound_dashboard/core/cross_alerts.py +540 -320
  53. truthound_dashboard/core/datasource_factory.py +1672 -0
  54. truthound_dashboard/core/drift_monitor.py +216 -20
  55. truthound_dashboard/core/enterprise_sampling.py +1291 -0
  56. truthound_dashboard/core/interfaces/__init__.py +225 -0
  57. truthound_dashboard/core/interfaces/actions.py +652 -0
  58. truthound_dashboard/core/interfaces/base.py +247 -0
  59. truthound_dashboard/core/interfaces/checkpoint.py +676 -0
  60. truthound_dashboard/core/interfaces/protocols.py +664 -0
  61. truthound_dashboard/core/interfaces/reporters.py +650 -0
  62. truthound_dashboard/core/interfaces/routing.py +646 -0
  63. truthound_dashboard/core/interfaces/triggers.py +619 -0
  64. truthound_dashboard/core/lineage.py +407 -71
  65. truthound_dashboard/core/model_monitoring.py +431 -3
  66. truthound_dashboard/core/notifications/base.py +4 -0
  67. truthound_dashboard/core/notifications/channels.py +501 -1203
  68. truthound_dashboard/core/notifications/deduplication/__init__.py +81 -115
  69. truthound_dashboard/core/notifications/deduplication/service.py +131 -348
  70. truthound_dashboard/core/notifications/dispatcher.py +202 -11
  71. truthound_dashboard/core/notifications/escalation/__init__.py +119 -106
  72. truthound_dashboard/core/notifications/escalation/engine.py +168 -358
  73. truthound_dashboard/core/notifications/routing/__init__.py +88 -128
  74. truthound_dashboard/core/notifications/routing/engine.py +90 -317
  75. truthound_dashboard/core/notifications/stats_aggregator.py +246 -1
  76. truthound_dashboard/core/notifications/throttling/__init__.py +67 -50
  77. truthound_dashboard/core/notifications/throttling/builder.py +117 -255
  78. truthound_dashboard/core/notifications/truthound_adapter.py +842 -0
  79. truthound_dashboard/core/phase5/collaboration.py +1 -1
  80. truthound_dashboard/core/plugins/lifecycle/__init__.py +0 -13
  81. truthound_dashboard/core/quality_reporter.py +1359 -0
  82. truthound_dashboard/core/report_history.py +0 -6
  83. truthound_dashboard/core/reporters/__init__.py +175 -14
  84. truthound_dashboard/core/reporters/adapters.py +943 -0
  85. truthound_dashboard/core/reporters/base.py +0 -3
  86. truthound_dashboard/core/reporters/builtin/__init__.py +18 -0
  87. truthound_dashboard/core/reporters/builtin/csv_reporter.py +111 -0
  88. truthound_dashboard/core/reporters/builtin/html_reporter.py +270 -0
  89. truthound_dashboard/core/reporters/builtin/json_reporter.py +127 -0
  90. truthound_dashboard/core/reporters/compat.py +266 -0
  91. truthound_dashboard/core/reporters/csv_reporter.py +2 -35
  92. truthound_dashboard/core/reporters/factory.py +526 -0
  93. truthound_dashboard/core/reporters/interfaces.py +745 -0
  94. truthound_dashboard/core/reporters/registry.py +1 -10
  95. truthound_dashboard/core/scheduler.py +165 -0
  96. truthound_dashboard/core/schema_evolution.py +3 -3
  97. truthound_dashboard/core/schema_watcher.py +1528 -0
  98. truthound_dashboard/core/services.py +595 -76
  99. truthound_dashboard/core/store_manager.py +810 -0
  100. truthound_dashboard/core/streaming_anomaly.py +169 -4
  101. truthound_dashboard/core/tiering.py +1309 -0
  102. truthound_dashboard/core/triggers/evaluators.py +178 -8
  103. truthound_dashboard/core/truthound_adapter.py +2620 -197
  104. truthound_dashboard/core/unified_alerts.py +23 -20
  105. truthound_dashboard/db/__init__.py +8 -0
  106. truthound_dashboard/db/database.py +8 -2
  107. truthound_dashboard/db/models.py +944 -25
  108. truthound_dashboard/db/repository.py +2 -0
  109. truthound_dashboard/main.py +11 -0
  110. truthound_dashboard/schemas/__init__.py +177 -16
  111. truthound_dashboard/schemas/base.py +44 -23
  112. truthound_dashboard/schemas/collaboration.py +19 -6
  113. truthound_dashboard/schemas/cross_alerts.py +19 -3
  114. truthound_dashboard/schemas/drift.py +61 -55
  115. truthound_dashboard/schemas/drift_monitor.py +67 -23
  116. truthound_dashboard/schemas/enterprise_sampling.py +653 -0
  117. truthound_dashboard/schemas/lineage.py +0 -33
  118. truthound_dashboard/schemas/mask.py +10 -8
  119. truthound_dashboard/schemas/model_monitoring.py +89 -10
  120. truthound_dashboard/schemas/notifications_advanced.py +13 -0
  121. truthound_dashboard/schemas/observability.py +453 -0
  122. truthound_dashboard/schemas/plugins.py +0 -280
  123. truthound_dashboard/schemas/profile.py +154 -247
  124. truthound_dashboard/schemas/quality_reporter.py +403 -0
  125. truthound_dashboard/schemas/reports.py +2 -2
  126. truthound_dashboard/schemas/rule_suggestion.py +8 -1
  127. truthound_dashboard/schemas/scan.py +4 -24
  128. truthound_dashboard/schemas/schedule.py +11 -3
  129. truthound_dashboard/schemas/schema_watcher.py +727 -0
  130. truthound_dashboard/schemas/source.py +17 -2
  131. truthound_dashboard/schemas/tiering.py +822 -0
  132. truthound_dashboard/schemas/triggers.py +16 -0
  133. truthound_dashboard/schemas/unified_alerts.py +7 -0
  134. truthound_dashboard/schemas/validation.py +0 -13
  135. truthound_dashboard/schemas/validators/base.py +41 -21
  136. truthound_dashboard/schemas/validators/business_rule_validators.py +244 -0
  137. truthound_dashboard/schemas/validators/localization_validators.py +273 -0
  138. truthound_dashboard/schemas/validators/ml_feature_validators.py +308 -0
  139. truthound_dashboard/schemas/validators/profiling_validators.py +275 -0
  140. truthound_dashboard/schemas/validators/referential_validators.py +312 -0
  141. truthound_dashboard/schemas/validators/registry.py +93 -8
  142. truthound_dashboard/schemas/validators/timeseries_validators.py +389 -0
  143. truthound_dashboard/schemas/versioning.py +1 -6
  144. truthound_dashboard/static/index.html +2 -2
  145. truthound_dashboard-1.5.0.dist-info/METADATA +309 -0
  146. {truthound_dashboard-1.4.3.dist-info → truthound_dashboard-1.5.0.dist-info}/RECORD +149 -148
  147. truthound_dashboard/core/plugins/hooks/__init__.py +0 -63
  148. truthound_dashboard/core/plugins/hooks/decorators.py +0 -367
  149. truthound_dashboard/core/plugins/hooks/manager.py +0 -403
  150. truthound_dashboard/core/plugins/hooks/protocols.py +0 -265
  151. truthound_dashboard/core/plugins/lifecycle/hot_reload.py +0 -584
  152. truthound_dashboard/core/reporters/junit_reporter.py +0 -233
  153. truthound_dashboard/core/reporters/markdown_reporter.py +0 -207
  154. truthound_dashboard/core/reporters/pdf_reporter.py +0 -209
  155. truthound_dashboard/static/assets/_baseUniq-BcrSP13d.js +0 -1
  156. truthound_dashboard/static/assets/arc-DlYjKwIL.js +0 -1
  157. truthound_dashboard/static/assets/architectureDiagram-VXUJARFQ-Bb2drbQM.js +0 -36
  158. truthound_dashboard/static/assets/blockDiagram-VD42YOAC-BlsPG1CH.js +0 -122
  159. truthound_dashboard/static/assets/c4Diagram-YG6GDRKO-B9JdUoaC.js +0 -10
  160. truthound_dashboard/static/assets/channel-Q6mHF1Hd.js +0 -1
  161. truthound_dashboard/static/assets/chunk-4BX2VUAB-DmyoPVuJ.js +0 -1
  162. truthound_dashboard/static/assets/chunk-55IACEB6-Bcz6Siv8.js +0 -1
  163. truthound_dashboard/static/assets/chunk-B4BG7PRW-Br3G5Rum.js +0 -165
  164. truthound_dashboard/static/assets/chunk-DI55MBZ5-DuM9c23u.js +0 -220
  165. truthound_dashboard/static/assets/chunk-FMBD7UC4-DNU-5mvT.js +0 -15
  166. truthound_dashboard/static/assets/chunk-QN33PNHL-Im2yNcmS.js +0 -1
  167. truthound_dashboard/static/assets/chunk-QZHKN3VN-kZr8XFm1.js +0 -1
  168. truthound_dashboard/static/assets/chunk-TZMSLE5B-Q__360q_.js +0 -1
  169. truthound_dashboard/static/assets/classDiagram-2ON5EDUG-vtixxUyK.js +0 -1
  170. truthound_dashboard/static/assets/classDiagram-v2-WZHVMYZB-vtixxUyK.js +0 -1
  171. truthound_dashboard/static/assets/clone-BOt2LwD0.js +0 -1
  172. truthound_dashboard/static/assets/cose-bilkent-S5V4N54A-CBDw6iac.js +0 -1
  173. truthound_dashboard/static/assets/dagre-6UL2VRFP-XdKqmmY9.js +0 -4
  174. truthound_dashboard/static/assets/diagram-PSM6KHXK-DAZ8nx9V.js +0 -24
  175. truthound_dashboard/static/assets/diagram-QEK2KX5R-BRvDTbGD.js +0 -43
  176. truthound_dashboard/static/assets/diagram-S2PKOQOG-bQcczUkl.js +0 -24
  177. truthound_dashboard/static/assets/erDiagram-Q2GNP2WA-DPje7VMN.js +0 -60
  178. truthound_dashboard/static/assets/flowDiagram-NV44I4VS-B7BVtFVS.js +0 -162
  179. truthound_dashboard/static/assets/ganttDiagram-JELNMOA3-D6WKSS7U.js +0 -267
  180. truthound_dashboard/static/assets/gitGraphDiagram-NY62KEGX-D3vtVd3y.js +0 -65
  181. truthound_dashboard/static/assets/graph-BKgNKZVp.js +0 -1
  182. truthound_dashboard/static/assets/index-C6JSrkHo.css +0 -1
  183. truthound_dashboard/static/assets/index-DkU82VsU.js +0 -1800
  184. truthound_dashboard/static/assets/infoDiagram-WHAUD3N6-DnNCT429.js +0 -2
  185. truthound_dashboard/static/assets/journeyDiagram-XKPGCS4Q-DGiMozqS.js +0 -139
  186. truthound_dashboard/static/assets/kanban-definition-3W4ZIXB7-BV2gUgli.js +0 -89
  187. truthound_dashboard/static/assets/katex-Cu_Erd72.js +0 -261
  188. truthound_dashboard/static/assets/layout-DI2MfQ5G.js +0 -1
  189. truthound_dashboard/static/assets/min-DYdgXVcT.js +0 -1
  190. truthound_dashboard/static/assets/mindmap-definition-VGOIOE7T-C7x4ruxz.js +0 -68
  191. truthound_dashboard/static/assets/pieDiagram-ADFJNKIX-CAJaAB9f.js +0 -30
  192. truthound_dashboard/static/assets/quadrantDiagram-AYHSOK5B-DeqwDI46.js +0 -7
  193. truthound_dashboard/static/assets/requirementDiagram-UZGBJVZJ-e3XDpZIM.js +0 -64
  194. truthound_dashboard/static/assets/sankeyDiagram-TZEHDZUN-CNnAv5Ux.js +0 -10
  195. truthound_dashboard/static/assets/sequenceDiagram-WL72ISMW-Dsne-Of3.js +0 -145
  196. truthound_dashboard/static/assets/stateDiagram-FKZM4ZOC-Ee0sQXyb.js +0 -1
  197. truthound_dashboard/static/assets/stateDiagram-v2-4FDKWEC3-B26KqW_W.js +0 -1
  198. truthound_dashboard/static/assets/timeline-definition-IT6M3QCI-DZYi2yl3.js +0 -61
  199. truthound_dashboard/static/assets/treemap-KMMF4GRG-CY3f8In2.js +0 -128
  200. truthound_dashboard/static/assets/unmerged_dictionaries-Dd7xcPWG.js +0 -1
  201. truthound_dashboard/static/assets/xychartDiagram-PRI3JC2R-CS7fydZZ.js +0 -7
  202. truthound_dashboard-1.4.3.dist-info/METADATA +0 -505
  203. {truthound_dashboard-1.4.3.dist-info → truthound_dashboard-1.5.0.dist-info}/WHEEL +0 -0
  204. {truthound_dashboard-1.4.3.dist-info → truthound_dashboard-1.5.0.dist-info}/entry_points.txt +0 -0
  205. {truthound_dashboard-1.4.3.dist-info → truthound_dashboard-1.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,451 @@
1
+ """Mock backend implementation for testing.
2
+
3
+ This module provides a mock backend that can be used for:
4
+ - Unit testing without truthound dependency
5
+ - Development when truthound is not installed
6
+ - Fallback when truthound is unavailable
7
+
8
+ The mock backend returns sensible default results for all operations.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from typing import Any
14
+
15
+ from truthound_dashboard.core.interfaces import DataInput
16
+ from truthound_dashboard.core.truthound_adapter import (
17
+ CheckResult,
18
+ ColumnProfileResult,
19
+ CompareResult,
20
+ GenerateSuiteResult,
21
+ LearnResult,
22
+ MaskResult,
23
+ ProfileResult,
24
+ ScanResult,
25
+ )
26
+
27
+ from .base import BaseDataQualityBackend
28
+
29
+
30
+ class MockBackend(BaseDataQualityBackend):
31
+ """Mock data quality backend for testing.
32
+
33
+ This backend provides predictable responses for all data quality
34
+ operations without requiring truthound or any actual data processing.
35
+
36
+ Use cases:
37
+ - Unit testing services without truthound
38
+ - Development environment setup
39
+ - Fallback when truthound is unavailable
40
+
41
+ Example:
42
+ backend = MockBackend()
43
+ result = await backend.check("data.csv")
44
+ assert result.passed == True # Mock always passes by default
45
+ """
46
+
47
+ def __init__(self, max_workers: int = 4, *, always_pass: bool = True) -> None:
48
+ """Initialize mock backend.
49
+
50
+ Args:
51
+ max_workers: Maximum worker threads (ignored in mock).
52
+ always_pass: If True, validation always passes.
53
+ """
54
+ super().__init__(max_workers=max_workers)
55
+ self._always_pass = always_pass
56
+
57
+ def is_available(self) -> bool:
58
+ """Mock is always available.
59
+
60
+ Returns:
61
+ Always True.
62
+ """
63
+ return True
64
+
65
+ def get_version(self) -> str | None:
66
+ """Get mock version.
67
+
68
+ Returns:
69
+ Mock version string.
70
+ """
71
+ return "mock-1.0.0"
72
+
73
+ async def check(
74
+ self,
75
+ data: DataInput,
76
+ *,
77
+ validators: list[str] | None = None,
78
+ validator_config: dict[str, dict[str, Any]] | None = None,
79
+ schema: str | None = None,
80
+ auto_schema: bool = False,
81
+ columns: list[str] | None = None,
82
+ min_severity: str | None = None,
83
+ strict: bool = False,
84
+ parallel: bool = False,
85
+ max_workers: int | None = None,
86
+ pushdown: bool | None = None,
87
+ ) -> CheckResult:
88
+ """Mock validation check.
89
+
90
+ Returns:
91
+ CheckResult with mock data.
92
+ """
93
+ source_name = data if isinstance(data, str) else getattr(data, "name", "mock")
94
+
95
+ if self._always_pass:
96
+ return CheckResult(
97
+ passed=True,
98
+ has_critical=False,
99
+ has_high=False,
100
+ total_issues=0,
101
+ critical_issues=0,
102
+ high_issues=0,
103
+ medium_issues=0,
104
+ low_issues=0,
105
+ source=source_name,
106
+ row_count=1000,
107
+ column_count=10,
108
+ issues=[],
109
+ )
110
+ else:
111
+ return CheckResult(
112
+ passed=False,
113
+ has_critical=False,
114
+ has_high=True,
115
+ total_issues=2,
116
+ critical_issues=0,
117
+ high_issues=1,
118
+ medium_issues=1,
119
+ low_issues=0,
120
+ source=source_name,
121
+ row_count=1000,
122
+ column_count=10,
123
+ issues=[
124
+ {
125
+ "column": "id",
126
+ "issue_type": "null_values",
127
+ "count": 10,
128
+ "severity": "high",
129
+ "details": "10 null values found",
130
+ },
131
+ {
132
+ "column": "email",
133
+ "issue_type": "invalid_format",
134
+ "count": 5,
135
+ "severity": "medium",
136
+ "details": "5 invalid email formats",
137
+ },
138
+ ],
139
+ )
140
+
141
+ async def learn(
142
+ self,
143
+ source: DataInput,
144
+ *,
145
+ infer_constraints: bool = True,
146
+ categorical_threshold: int | None = None,
147
+ sample_size: int | None = None,
148
+ ) -> LearnResult:
149
+ """Mock schema learning.
150
+
151
+ Returns:
152
+ LearnResult with mock schema.
153
+ """
154
+ mock_schema = {
155
+ "version": "1.0",
156
+ "row_count": 1000,
157
+ "columns": {
158
+ "id": {
159
+ "name": "id",
160
+ "dtype": "Int64",
161
+ "nullable": False,
162
+ "unique": True,
163
+ },
164
+ "name": {
165
+ "name": "name",
166
+ "dtype": "String",
167
+ "nullable": True,
168
+ },
169
+ "value": {
170
+ "name": "value",
171
+ "dtype": "Float64",
172
+ "nullable": True,
173
+ "min_value": 0.0,
174
+ "max_value": 100.0,
175
+ },
176
+ },
177
+ }
178
+
179
+ import yaml
180
+ schema_yaml = yaml.dump(mock_schema, default_flow_style=False)
181
+
182
+ return LearnResult(
183
+ schema=mock_schema,
184
+ schema_yaml=schema_yaml,
185
+ row_count=1000,
186
+ column_count=3,
187
+ columns=["id", "name", "value"],
188
+ )
189
+
190
+ async def profile(
191
+ self,
192
+ source: DataInput,
193
+ *,
194
+ sample_size: int | None = None,
195
+ include_patterns: bool = True,
196
+ include_correlations: bool = False,
197
+ include_distributions: bool = True,
198
+ top_n_values: int = 10,
199
+ pattern_sample_size: int = 1000,
200
+ correlation_threshold: float = 0.7,
201
+ min_pattern_match_ratio: float = 0.8,
202
+ n_jobs: int = 1,
203
+ ) -> ProfileResult:
204
+ """Mock data profiling.
205
+
206
+ Returns:
207
+ ProfileResult with mock profile data.
208
+ """
209
+ source_name = source if isinstance(source, str) else getattr(source, "name", "mock")
210
+
211
+ columns = [
212
+ ColumnProfileResult(
213
+ name="id",
214
+ physical_type="Int64",
215
+ inferred_type="integer",
216
+ row_count=1000,
217
+ null_count=0,
218
+ null_ratio=0.0,
219
+ distinct_count=1000,
220
+ unique_ratio=1.0,
221
+ is_unique=True,
222
+ distribution={"min": 1, "max": 1000, "mean": 500.5},
223
+ ),
224
+ ColumnProfileResult(
225
+ name="name",
226
+ physical_type="String",
227
+ inferred_type="string",
228
+ row_count=1000,
229
+ null_count=50,
230
+ null_ratio=0.05,
231
+ distinct_count=800,
232
+ unique_ratio=0.8,
233
+ min_length=1,
234
+ max_length=100,
235
+ avg_length=25.5,
236
+ ),
237
+ ColumnProfileResult(
238
+ name="email",
239
+ physical_type="String",
240
+ inferred_type="email",
241
+ row_count=1000,
242
+ null_count=10,
243
+ null_ratio=0.01,
244
+ distinct_count=990,
245
+ unique_ratio=0.99,
246
+ detected_patterns=[
247
+ {
248
+ "pattern": "email",
249
+ "regex": r"^[\w.+-]+@[\w-]+\.[\w.-]+$",
250
+ "match_ratio": 0.99,
251
+ }
252
+ ],
253
+ ),
254
+ ]
255
+
256
+ return ProfileResult(
257
+ name=source_name,
258
+ source=source_name,
259
+ row_count=1000,
260
+ column_count=3,
261
+ estimated_memory_bytes=100000,
262
+ columns=columns,
263
+ duplicate_row_count=0,
264
+ duplicate_row_ratio=0.0,
265
+ correlations=None,
266
+ profiled_at="2024-01-01T00:00:00",
267
+ profile_duration_ms=100.0,
268
+ size_bytes=100000,
269
+ )
270
+
271
+ async def compare(
272
+ self,
273
+ baseline: DataInput,
274
+ current: DataInput,
275
+ *,
276
+ columns: list[str] | None = None,
277
+ method: str = "auto",
278
+ threshold: float | None = None,
279
+ sample_size: int | None = None,
280
+ ) -> CompareResult:
281
+ """Mock drift comparison.
282
+
283
+ Returns:
284
+ CompareResult with mock drift data.
285
+ """
286
+ baseline_name = baseline if isinstance(baseline, str) else getattr(baseline, "name", "baseline")
287
+ current_name = current if isinstance(current, str) else getattr(current, "name", "current")
288
+
289
+ return CompareResult(
290
+ baseline_source=baseline_name,
291
+ current_source=current_name,
292
+ baseline_rows=1000,
293
+ current_rows=1100,
294
+ has_drift=True,
295
+ has_high_drift=False,
296
+ total_columns=3,
297
+ drifted_columns=["value"],
298
+ columns=[
299
+ {
300
+ "column": "id",
301
+ "dtype": "Int64",
302
+ "drifted": False,
303
+ "level": "none",
304
+ "method": "ks",
305
+ "statistic": 0.02,
306
+ "p_value": 0.85,
307
+ },
308
+ {
309
+ "column": "name",
310
+ "dtype": "String",
311
+ "drifted": False,
312
+ "level": "none",
313
+ "method": "chi2",
314
+ "statistic": 5.2,
315
+ "p_value": 0.15,
316
+ },
317
+ {
318
+ "column": "value",
319
+ "dtype": "Float64",
320
+ "drifted": True,
321
+ "level": "medium",
322
+ "method": "psi",
323
+ "statistic": 0.15,
324
+ "p_value": 0.03,
325
+ },
326
+ ],
327
+ )
328
+
329
+ async def scan(
330
+ self,
331
+ data: DataInput,
332
+ *,
333
+ columns: list[str] | None = None,
334
+ regulations: list[str] | None = None,
335
+ min_confidence: float = 0.8,
336
+ ) -> ScanResult:
337
+ """Mock PII scan.
338
+
339
+ Returns:
340
+ ScanResult with mock PII findings.
341
+ """
342
+ source_name = data if isinstance(data, str) else getattr(data, "name", "mock")
343
+
344
+ return ScanResult(
345
+ source=source_name,
346
+ row_count=1000,
347
+ column_count=5,
348
+ total_columns_scanned=5,
349
+ columns_with_pii=2,
350
+ total_findings=2,
351
+ has_violations=False,
352
+ total_violations=0,
353
+ findings=[
354
+ {
355
+ "column": "email",
356
+ "pii_type": "email",
357
+ "confidence": 0.95,
358
+ "sample_count": 990,
359
+ },
360
+ {
361
+ "column": "phone",
362
+ "pii_type": "phone_number",
363
+ "confidence": 0.88,
364
+ "sample_count": 500,
365
+ },
366
+ ],
367
+ violations=[],
368
+ )
369
+
370
+ async def mask(
371
+ self,
372
+ data: DataInput,
373
+ output: str,
374
+ *,
375
+ columns: list[str] | None = None,
376
+ strategy: str = "redact",
377
+ ) -> MaskResult:
378
+ """Mock data masking.
379
+
380
+ Returns:
381
+ MaskResult with mock masking info.
382
+ """
383
+ source_name = data if isinstance(data, str) else getattr(data, "name", "mock")
384
+
385
+ return MaskResult(
386
+ source=source_name,
387
+ output_path=output,
388
+ row_count=1000,
389
+ column_count=5,
390
+ columns_masked=columns or ["email", "phone"],
391
+ strategy=strategy,
392
+ original_columns=["id", "name", "email", "phone", "value"],
393
+ )
394
+
395
+ async def generate_suite(
396
+ self,
397
+ profile: ProfileResult | dict[str, Any],
398
+ *,
399
+ strictness: str = "medium",
400
+ preset: str = "default",
401
+ include: list[str] | None = None,
402
+ exclude: list[str] | None = None,
403
+ output_format: str = "yaml",
404
+ ) -> GenerateSuiteResult:
405
+ """Mock suite generation.
406
+
407
+ Returns:
408
+ GenerateSuiteResult with mock rules.
409
+ """
410
+ rules = [
411
+ {
412
+ "name": "id_not_null",
413
+ "validator": "NotNull",
414
+ "column": "id",
415
+ "params": {},
416
+ "severity": "critical",
417
+ "category": "completeness",
418
+ },
419
+ {
420
+ "name": "id_unique",
421
+ "validator": "Unique",
422
+ "column": "id",
423
+ "params": {},
424
+ "severity": "critical",
425
+ "category": "uniqueness",
426
+ },
427
+ {
428
+ "name": "email_format",
429
+ "validator": "Regex",
430
+ "column": "email",
431
+ "params": {"pattern": r"^[\w.+-]+@[\w-]+\.[\w.-]+$"},
432
+ "severity": "medium",
433
+ "category": "format",
434
+ },
435
+ ]
436
+
437
+ yaml_content = "rules:\n"
438
+ for rule in rules:
439
+ yaml_content += f" - name: {rule['name']}\n"
440
+ yaml_content += f" validator: {rule['validator']}\n"
441
+ if rule.get("column"):
442
+ yaml_content += f" column: {rule['column']}\n"
443
+
444
+ return GenerateSuiteResult(
445
+ rules=rules,
446
+ rule_count=len(rules),
447
+ categories=["completeness", "uniqueness", "format"],
448
+ strictness=strictness,
449
+ yaml_content=yaml_content,
450
+ json_content={"rules": rules},
451
+ )