truthound-dashboard 1.4.4__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.4.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.4.dist-info/METADATA +0 -507
  203. {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/WHEEL +0 -0
  204. {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/entry_points.txt +0 -0
  205. {truthound_dashboard-1.4.4.dist-info → truthound_dashboard-1.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,403 @@
1
+ """Quality Reporter schemas for API request/response validation.
2
+
3
+ This module provides Pydantic schemas for the Quality Reporter feature,
4
+ enabling comprehensive quality assessment and reporting of validation rules.
5
+
6
+ Based on truthound's QualityReporter module:
7
+ - Quality scoring (F1, precision, recall, accuracy)
8
+ - Quality levels (excellent, good, acceptable, poor, unacceptable)
9
+ - Filtering and comparison capabilities
10
+ - Multiple report formats (console, json, html, markdown, junit)
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from datetime import datetime
16
+ from enum import Enum
17
+ from typing import Any, Literal
18
+
19
+ from pydantic import Field
20
+
21
+ from .base import BaseSchema, IDMixin, PaginatedResponse, TimestampMixin
22
+
23
+
24
+ # =============================================================================
25
+ # Enums
26
+ # =============================================================================
27
+
28
+
29
+ class QualityLevel(str, Enum):
30
+ """Quality levels for rules based on F1 score."""
31
+
32
+ EXCELLENT = "excellent" # 0.9 - 1.0
33
+ GOOD = "good" # 0.7 - 0.9
34
+ ACCEPTABLE = "acceptable" # 0.5 - 0.7
35
+ POOR = "poor" # 0.3 - 0.5
36
+ UNACCEPTABLE = "unacceptable" # 0.0 - 0.3
37
+
38
+
39
+ class QualityReportFormat(str, Enum):
40
+ """Available quality report formats."""
41
+
42
+ CONSOLE = "console"
43
+ JSON = "json"
44
+ HTML = "html"
45
+ MARKDOWN = "markdown"
46
+ JUNIT = "junit"
47
+
48
+
49
+ class QualityReportStatus(str, Enum):
50
+ """Status of quality report generation."""
51
+
52
+ PENDING = "pending"
53
+ GENERATING = "generating"
54
+ COMPLETED = "completed"
55
+ FAILED = "failed"
56
+
57
+
58
+ class ReportSortOrder(str, Enum):
59
+ """Sort order for quality scores."""
60
+
61
+ F1_DESC = "f1_desc"
62
+ F1_ASC = "f1_asc"
63
+ PRECISION_DESC = "precision_desc"
64
+ PRECISION_ASC = "precision_asc"
65
+ RECALL_DESC = "recall_desc"
66
+ RECALL_ASC = "recall_asc"
67
+ LEVEL_DESC = "level_desc"
68
+ LEVEL_ASC = "level_asc"
69
+ NAME_ASC = "name_asc"
70
+ NAME_DESC = "name_desc"
71
+
72
+
73
+ class QualityDisplayMode(str, Enum):
74
+ """Display mode for quality reports."""
75
+
76
+ SUMMARY = "summary"
77
+ DETAILED = "detailed"
78
+ COMPARISON = "comparison"
79
+ TREND = "trend"
80
+
81
+
82
+ class ChartType(str, Enum):
83
+ """Chart types for quality visualizations."""
84
+
85
+ BAR = "bar"
86
+ LINE = "line"
87
+ PIE = "pie"
88
+ RADAR = "radar"
89
+ HEATMAP = "heatmap"
90
+ SCATTER = "scatter"
91
+
92
+
93
+ # =============================================================================
94
+ # Confusion Matrix
95
+ # =============================================================================
96
+
97
+
98
+ class ConfusionMatrixSchema(BaseSchema):
99
+ """Confusion matrix for rule evaluation."""
100
+
101
+ true_positive: int = Field(default=0, ge=0, description="Correct violation detections")
102
+ true_negative: int = Field(default=0, ge=0, description="Correct passes")
103
+ false_positive: int = Field(default=0, ge=0, description="Incorrect violation detections (false alarms)")
104
+ false_negative: int = Field(default=0, ge=0, description="Missed violations")
105
+
106
+ @property
107
+ def precision(self) -> float:
108
+ """Precision: TP / (TP + FP)."""
109
+ total = self.true_positive + self.false_positive
110
+ return self.true_positive / total if total > 0 else 0.0
111
+
112
+ @property
113
+ def recall(self) -> float:
114
+ """Recall: TP / (TP + FN)."""
115
+ total = self.true_positive + self.false_negative
116
+ return self.true_positive / total if total > 0 else 0.0
117
+
118
+ @property
119
+ def f1_score(self) -> float:
120
+ """F1 score: harmonic mean of precision and recall."""
121
+ p, r = self.precision, self.recall
122
+ return 2 * (p * r) / (p + r) if (p + r) > 0 else 0.0
123
+
124
+ @property
125
+ def accuracy(self) -> float:
126
+ """Accuracy: (TP + TN) / Total."""
127
+ total = (
128
+ self.true_positive
129
+ + self.true_negative
130
+ + self.false_positive
131
+ + self.false_negative
132
+ )
133
+ return (self.true_positive + self.true_negative) / total if total > 0 else 0.0
134
+
135
+
136
+ # =============================================================================
137
+ # Quality Metrics
138
+ # =============================================================================
139
+
140
+
141
+ class QualityMetricsSchema(BaseSchema):
142
+ """Quality metrics for a validation rule."""
143
+
144
+ f1_score: float = Field(..., ge=0.0, le=1.0, description="F1 score (0-1)")
145
+ precision: float = Field(..., ge=0.0, le=1.0, description="Precision (0-1)")
146
+ recall: float = Field(..., ge=0.0, le=1.0, description="Recall (0-1)")
147
+ accuracy: float = Field(..., ge=0.0, le=1.0, description="Accuracy (0-1)")
148
+ confidence: float = Field(default=0.0, ge=0.0, le=1.0, description="Confidence score (0-1)")
149
+ quality_level: QualityLevel = Field(..., description="Computed quality level")
150
+
151
+
152
+ class QualityThresholdsSchema(BaseSchema):
153
+ """Configurable thresholds for quality levels."""
154
+
155
+ excellent: float = Field(default=0.9, ge=0.0, le=1.0, description="Minimum for excellent")
156
+ good: float = Field(default=0.7, ge=0.0, le=1.0, description="Minimum for good")
157
+ acceptable: float = Field(default=0.5, ge=0.0, le=1.0, description="Minimum for acceptable")
158
+ poor: float = Field(default=0.3, ge=0.0, le=1.0, description="Minimum for poor")
159
+
160
+
161
+ # =============================================================================
162
+ # Quality Score
163
+ # =============================================================================
164
+
165
+
166
+ class QualityScoreSchema(BaseSchema):
167
+ """Quality score result for a single rule."""
168
+
169
+ rule_name: str = Field(..., description="Name of the validation rule")
170
+ rule_type: str | None = Field(default=None, description="Type of rule (pattern, range, etc.)")
171
+ column: str | None = Field(default=None, description="Target column name")
172
+ metrics: QualityMetricsSchema = Field(..., description="Quality metrics")
173
+ confusion_matrix: ConfusionMatrixSchema | None = Field(default=None, description="Confusion matrix details")
174
+ test_sample_size: int = Field(default=0, ge=0, description="Number of samples tested")
175
+ evaluation_time_ms: float = Field(default=0.0, ge=0.0, description="Evaluation time in milliseconds")
176
+ recommendation: str | None = Field(default=None, description="Recommendation message")
177
+ should_use: bool = Field(default=True, description="Whether the rule should be used")
178
+ issues: list[dict[str, Any]] = Field(default_factory=list, description="List of identified issues")
179
+
180
+
181
+ # =============================================================================
182
+ # Quality Statistics
183
+ # =============================================================================
184
+
185
+
186
+ class QualityStatisticsSchema(BaseSchema):
187
+ """Aggregate statistics for quality scores."""
188
+
189
+ total_count: int = Field(default=0, ge=0, description="Total number of rules")
190
+ excellent_count: int = Field(default=0, ge=0, description="Count of excellent rules")
191
+ good_count: int = Field(default=0, ge=0, description="Count of good rules")
192
+ acceptable_count: int = Field(default=0, ge=0, description="Count of acceptable rules")
193
+ poor_count: int = Field(default=0, ge=0, description="Count of poor rules")
194
+ unacceptable_count: int = Field(default=0, ge=0, description="Count of unacceptable rules")
195
+ should_use_count: int = Field(default=0, ge=0, description="Count of recommended rules")
196
+ avg_f1: float = Field(default=0.0, ge=0.0, le=1.0, description="Average F1 score")
197
+ min_f1: float = Field(default=0.0, ge=0.0, le=1.0, description="Minimum F1 score")
198
+ max_f1: float = Field(default=0.0, ge=0.0, le=1.0, description="Maximum F1 score")
199
+ avg_precision: float = Field(default=0.0, ge=0.0, le=1.0, description="Average precision")
200
+ avg_recall: float = Field(default=0.0, ge=0.0, le=1.0, description="Average recall")
201
+ avg_confidence: float = Field(default=0.0, ge=0.0, le=1.0, description="Average confidence")
202
+
203
+
204
+ class QualityLevelDistribution(BaseSchema):
205
+ """Distribution of quality levels."""
206
+
207
+ level: QualityLevel = Field(..., description="Quality level")
208
+ count: int = Field(..., ge=0, description="Number of rules at this level")
209
+ percentage: float = Field(..., ge=0.0, le=100.0, description="Percentage of total")
210
+
211
+
212
+ # =============================================================================
213
+ # Request Schemas
214
+ # =============================================================================
215
+
216
+
217
+ class QualityScoreRequest(BaseSchema):
218
+ """Request to score validation rules."""
219
+
220
+ source_id: str | None = Field(default=None, description="Source ID to score rules for")
221
+ validation_id: str | None = Field(default=None, description="Validation ID to score")
222
+ rule_names: list[str] | None = Field(default=None, description="Specific rules to score (optional)")
223
+ sample_size: int = Field(default=10000, ge=100, le=1000000, description="Sample size for scoring")
224
+ thresholds: QualityThresholdsSchema | None = Field(default=None, description="Custom quality thresholds")
225
+
226
+
227
+ class QualityFilterRequest(BaseSchema):
228
+ """Request to filter quality scores."""
229
+
230
+ min_level: QualityLevel | None = Field(default=None, description="Minimum quality level")
231
+ max_level: QualityLevel | None = Field(default=None, description="Maximum quality level")
232
+ min_f1: float | None = Field(default=None, ge=0.0, le=1.0, description="Minimum F1 score")
233
+ max_f1: float | None = Field(default=None, ge=0.0, le=1.0, description="Maximum F1 score")
234
+ min_confidence: float | None = Field(default=None, ge=0.0, le=1.0, description="Minimum confidence")
235
+ should_use_only: bool = Field(default=False, description="Only include recommended rules")
236
+ include_columns: list[str] | None = Field(default=None, description="Filter by specific columns")
237
+ exclude_columns: list[str] | None = Field(default=None, description="Exclude specific columns")
238
+ rule_types: list[str] | None = Field(default=None, description="Filter by rule types")
239
+
240
+
241
+ class QualityReportConfigSchema(BaseSchema):
242
+ """Configuration for quality report generation."""
243
+
244
+ title: str | None = Field(default=None, description="Report title")
245
+ description: str | None = Field(default=None, description="Report description")
246
+
247
+ # Content settings
248
+ include_metrics: bool = Field(default=True, description="Include detailed metrics")
249
+ include_confusion_matrix: bool = Field(default=False, description="Include confusion matrices")
250
+ include_recommendations: bool = Field(default=True, description="Include recommendations")
251
+ include_statistics: bool = Field(default=True, description="Include aggregate statistics")
252
+ include_summary: bool = Field(default=True, description="Include summary section")
253
+ include_charts: bool = Field(default=True, description="Include visual charts (HTML only)")
254
+
255
+ # Formatting
256
+ metric_precision: int = Field(default=2, ge=0, le=6, description="Decimal places for metrics")
257
+ percentage_format: bool = Field(default=True, description="Display as percentages")
258
+
259
+ # Display
260
+ sort_order: ReportSortOrder = Field(
261
+ default=ReportSortOrder.F1_DESC, description="Sort order for scores"
262
+ )
263
+ max_scores: int | None = Field(default=None, ge=1, description="Maximum scores to include")
264
+
265
+ # HTML-specific
266
+ theme: Literal["light", "dark", "professional"] = Field(
267
+ default="professional", description="Theme for HTML reports"
268
+ )
269
+
270
+
271
+ class QualityReportGenerateRequest(BaseSchema):
272
+ """Request to generate a quality report."""
273
+
274
+ source_id: str | None = Field(default=None, description="Source ID for the report")
275
+ validation_id: str | None = Field(default=None, description="Validation ID for the report")
276
+ format: QualityReportFormat = Field(
277
+ default=QualityReportFormat.HTML, description="Report format"
278
+ )
279
+ config: QualityReportConfigSchema | None = Field(default=None, description="Report configuration")
280
+ filter: QualityFilterRequest | None = Field(default=None, description="Score filter")
281
+ score_rules: bool = Field(
282
+ default=True, description="Score rules before generating report"
283
+ )
284
+ sample_size: int = Field(default=10000, ge=100, le=1000000, description="Sample size for scoring")
285
+
286
+
287
+ class QualityCompareRequest(BaseSchema):
288
+ """Request to compare quality scores."""
289
+
290
+ score_ids: list[str] | None = Field(default=None, description="Score IDs to compare")
291
+ source_ids: list[str] | None = Field(default=None, description="Source IDs to compare")
292
+ sort_by: Literal["f1_score", "precision", "recall", "confidence"] = Field(
293
+ default="f1_score", description="Metric to sort by"
294
+ )
295
+ descending: bool = Field(default=True, description="Sort in descending order")
296
+ group_by: Literal["column", "level", "rule_type"] | None = Field(
297
+ default=None, description="Group results by"
298
+ )
299
+ max_results: int = Field(default=50, ge=1, le=500, description="Maximum results")
300
+
301
+
302
+ # =============================================================================
303
+ # Response Schemas
304
+ # =============================================================================
305
+
306
+
307
+ class QualityScoreResponse(BaseSchema, IDMixin, TimestampMixin):
308
+ """Response for a quality score."""
309
+
310
+ source_id: str = Field(..., description="Source ID")
311
+ source_name: str | None = Field(default=None, description="Source name")
312
+ validation_id: str | None = Field(default=None, description="Associated validation ID")
313
+ status: QualityReportStatus = Field(..., description="Score status")
314
+ scores: list[QualityScoreSchema] = Field(default_factory=list, description="Individual rule scores")
315
+ statistics: QualityStatisticsSchema | None = Field(default=None, description="Aggregate statistics")
316
+ level_distribution: list[QualityLevelDistribution] | None = Field(
317
+ default=None, description="Quality level distribution"
318
+ )
319
+ sample_size: int = Field(default=0, ge=0, description="Sample size used")
320
+ evaluation_time_ms: float = Field(default=0.0, ge=0.0, description="Total evaluation time")
321
+ error_message: str | None = Field(default=None, description="Error message if failed")
322
+
323
+
324
+ class QualityReportResponse(BaseSchema, IDMixin, TimestampMixin):
325
+ """Response for a generated quality report."""
326
+
327
+ source_id: str | None = Field(default=None, description="Source ID")
328
+ source_name: str | None = Field(default=None, description="Source name")
329
+ validation_id: str | None = Field(default=None, description="Validation ID")
330
+ format: QualityReportFormat = Field(..., description="Report format")
331
+ status: QualityReportStatus = Field(..., description="Report status")
332
+ filename: str | None = Field(default=None, description="Generated filename")
333
+ file_path: str | None = Field(default=None, description="File path")
334
+ file_size_bytes: int | None = Field(default=None, ge=0, description="File size in bytes")
335
+ content_type: str | None = Field(default=None, description="Content type")
336
+ generation_time_ms: float | None = Field(default=None, ge=0.0, description="Generation time")
337
+ scores_count: int = Field(default=0, ge=0, description="Number of scores in report")
338
+ statistics: QualityStatisticsSchema | None = Field(default=None, description="Report statistics")
339
+ error_message: str | None = Field(default=None, description="Error message if failed")
340
+ download_count: int = Field(default=0, ge=0, description="Number of downloads")
341
+ expires_at: datetime | None = Field(default=None, description="Expiration timestamp")
342
+
343
+
344
+ class QualityCompareResponse(BaseSchema):
345
+ """Response for quality comparison."""
346
+
347
+ scores: list[QualityScoreSchema] = Field(..., description="Compared scores")
348
+ ranked_by: str = Field(..., description="Metric used for ranking")
349
+ best_rule: QualityScoreSchema | None = Field(default=None, description="Best performing rule")
350
+ worst_rule: QualityScoreSchema | None = Field(default=None, description="Worst performing rule")
351
+ groups: dict[str, list[QualityScoreSchema]] | None = Field(
352
+ default=None, description="Grouped scores"
353
+ )
354
+ statistics: QualityStatisticsSchema | None = Field(default=None, description="Comparison statistics")
355
+
356
+
357
+ class QualitySummaryResponse(BaseSchema):
358
+ """Summary response for quality scores."""
359
+
360
+ total_rules: int = Field(..., ge=0, description="Total rules scored")
361
+ statistics: QualityStatisticsSchema = Field(..., description="Quality statistics")
362
+ level_distribution: list[QualityLevelDistribution] = Field(
363
+ ..., description="Level distribution"
364
+ )
365
+ recommendations: dict[str, int] = Field(
366
+ ..., description="Recommendation counts (should_use, should_not_use)"
367
+ )
368
+ metric_averages: dict[str, dict[str, float]] = Field(
369
+ ..., description="Metric averages (avg, min, max)"
370
+ )
371
+
372
+
373
+ # =============================================================================
374
+ # List Response Types
375
+ # =============================================================================
376
+
377
+
378
+ class QualityScoreListResponse(PaginatedResponse[QualityScoreResponse]):
379
+ """Paginated list of quality scores."""
380
+
381
+ pass
382
+
383
+
384
+ class QualityReportListResponse(PaginatedResponse[QualityReportResponse]):
385
+ """Paginated list of quality reports."""
386
+
387
+ pass
388
+
389
+
390
+ # =============================================================================
391
+ # Available Formats Response
392
+ # =============================================================================
393
+
394
+
395
+ class QualityFormatsResponse(BaseSchema):
396
+ """Available quality report formats and options."""
397
+
398
+ formats: list[str] = Field(..., description="Available report formats")
399
+ sort_orders: list[str] = Field(..., description="Available sort orders")
400
+ themes: list[str] = Field(..., description="Available themes")
401
+ default_thresholds: QualityThresholdsSchema = Field(
402
+ ..., description="Default quality thresholds"
403
+ )
@@ -14,7 +14,7 @@ from pydantic import Field
14
14
  from .base import BaseSchema, IDMixin, ListResponseWrapper, TimestampMixin
15
15
 
16
16
  # Report format types
17
- ReportFormatType = Literal["html", "csv", "json", "markdown", "pdf", "junit", "excel", "custom"]
17
+ ReportFormatType = Literal["html", "csv", "json", "custom"]
18
18
 
19
19
  # Report theme types
20
20
  ReportThemeType = Literal["light", "dark", "professional", "minimal", "high_contrast"]
@@ -96,7 +96,7 @@ class AvailableFormatsResponse(BaseSchema):
96
96
  formats: list[str] = Field(
97
97
  ...,
98
98
  description="List of available format names",
99
- examples=[["html", "csv", "json", "markdown"]],
99
+ examples=[["html", "csv", "json"]],
100
100
  )
101
101
  themes: list[str] = Field(
102
102
  ...,
@@ -115,6 +115,10 @@ class CrossColumnRuleType(str, Enum):
115
115
  class SuggestedRule(BaseSchema):
116
116
  """A single suggested validation rule."""
117
117
 
118
+ id: str = Field(
119
+ default_factory=lambda: str(__import__("uuid").uuid4()),
120
+ description="Unique identifier for the suggestion",
121
+ )
118
122
  column: str = Field(..., description="Target column name (or primary column for multi-column rules)")
119
123
  validator_name: str = Field(..., description="Validator to apply")
120
124
  params: dict[str, Any] = Field(
@@ -153,7 +157,10 @@ class SuggestedRule(BaseSchema):
153
157
  class CrossColumnRuleSuggestion(BaseSchema):
154
158
  """A suggested cross-column validation rule with detailed relationship info."""
155
159
 
156
- id: str = Field(default_factory=lambda: "", description="Unique suggestion ID")
160
+ id: str = Field(
161
+ default_factory=lambda: str(__import__("uuid").uuid4()),
162
+ description="Unique suggestion ID",
163
+ )
157
164
  rule_type: CrossColumnRuleType = Field(..., description="Type of cross-column rule")
158
165
  columns: list[str] = Field(..., description="Columns involved in the relationship")
159
166
  validator_name: str = Field(..., description="Validator to apply")
@@ -50,32 +50,12 @@ PII_TYPES = [
50
50
  class PIIScanRequest(BaseSchema):
51
51
  """Request to run PII scan on a data source.
52
52
 
53
- This schema maps to truthound's th.scan() parameters for maximum flexibility.
54
- All optional parameters default to None to use truthound's defaults.
53
+ Note: truthound's th.scan() does not support any configuration parameters.
54
+ The scan runs on all columns with default settings.
55
+ This schema is kept for API compatibility but options are not used.
55
56
  """
56
57
 
57
- # Column filtering
58
- columns: list[str] | None = Field(
59
- default=None,
60
- description="Columns to scan. If None, all columns are scanned.",
61
- examples=[["email", "phone", "ssn"]],
62
- )
63
-
64
- # Regulation compliance checking
65
- regulations: list[RegulationLiteral] | None = Field(
66
- default=None,
67
- description="Privacy regulations to check compliance: gdpr, ccpa, lgpd",
68
- examples=[["gdpr", "ccpa"]],
69
- )
70
-
71
- # Confidence threshold
72
- min_confidence: float = Field(
73
- default=0.8,
74
- ge=0.0,
75
- le=1.0,
76
- description="Minimum confidence threshold for PII detection (0.0-1.0)",
77
- examples=[0.8, 0.9],
78
- )
58
+ pass
79
59
 
80
60
 
81
61
  class PIIFinding(BaseSchema):
@@ -28,6 +28,8 @@ class ScheduleCreate(ScheduleBase):
28
28
  """Request body for creating a schedule."""
29
29
 
30
30
  source_id: str = Field(..., description="Source ID to schedule")
31
+ trigger_type: str = Field("cron", description="Trigger type (cron, interval, data_change, composite, event, webhook, manual)")
32
+ trigger_config: dict[str, Any] | None = Field(None, description="Trigger-specific configuration")
31
33
  config: dict[str, Any] | None = Field(
32
34
  None,
33
35
  description="Additional configuration (validators, schema_path, etc.)",
@@ -39,6 +41,8 @@ class ScheduleUpdate(BaseModel):
39
41
 
40
42
  name: str | None = Field(None, min_length=1, max_length=255)
41
43
  cron_expression: str | None = Field(None)
44
+ trigger_type: str | None = Field(None, description="Trigger type")
45
+ trigger_config: dict[str, Any] | None = Field(None, description="Trigger-specific configuration")
42
46
  notify_on_failure: bool | None = Field(None)
43
47
  config: dict[str, Any] | None = Field(None)
44
48
 
@@ -49,6 +53,8 @@ class ScheduleResponse(BaseModel, IDMixin, TimestampMixin):
49
53
  name: str = Field(..., description="Schedule name")
50
54
  source_id: str = Field(..., description="Source ID")
51
55
  cron_expression: str = Field(..., description="Cron expression")
56
+ trigger_type: str = Field("cron", description="Trigger type")
57
+ trigger_config: dict[str, Any] | None = Field(None, description="Trigger-specific configuration")
52
58
  is_active: bool = Field(..., description="Whether schedule is active")
53
59
  notify_on_failure: bool = Field(..., description="Notify on failure")
54
60
  last_run_at: str | None = Field(None, description="Last run timestamp (ISO)")
@@ -65,6 +71,8 @@ class ScheduleListItem(BaseModel, IDMixin, TimestampMixin):
65
71
  name: str
66
72
  source_id: str
67
73
  cron_expression: str
74
+ trigger_type: str = "cron"
75
+ trigger_config: dict[str, Any] | None = None
68
76
  is_active: bool
69
77
  notify_on_failure: bool
70
78
  last_run_at: str | None = None
@@ -73,16 +81,16 @@ class ScheduleListItem(BaseModel, IDMixin, TimestampMixin):
73
81
 
74
82
 
75
83
  class ScheduleListResponse(BaseModel):
76
- """List response for schedules."""
84
+ """Paginated list response for schedules."""
77
85
 
78
- success: bool = True
79
86
  data: list[ScheduleListItem] = Field(default_factory=list)
80
87
  total: int = 0
88
+ offset: int = 0
89
+ limit: int = 100
81
90
 
82
91
 
83
92
  class ScheduleActionResponse(BaseModel):
84
93
  """Response for schedule actions (pause, resume, run)."""
85
94
 
86
- success: bool = True
87
95
  message: str = Field(..., description="Action result message")
88
96
  schedule: ScheduleResponse | None = Field(None, description="Updated schedule")