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,266 @@
1
+ """Compatibility bridge between old and new reporter APIs.
2
+
3
+ This module provides backward compatibility with the existing reporter
4
+ system while enabling gradual migration to the new interface-based
5
+ architecture.
6
+
7
+ The bridge handles:
8
+ 1. Converting old Validation models to new ReportData format
9
+ 2. Adapting new reporters to work with old generate_report calls
10
+ 3. Providing legacy generate_report function that uses new infrastructure
11
+
12
+ Example:
13
+ # Old style (still works)
14
+ from truthound_dashboard.core.reporters import generate_report
15
+ report = await generate_report(validation, format="html")
16
+
17
+ # New style (recommended)
18
+ from truthound_dashboard.core.reporters.factory import (
19
+ generate_report_from_validation
20
+ )
21
+ output = await generate_report_from_validation(validation, format="html")
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import logging
27
+ from typing import TYPE_CHECKING, Any
28
+
29
+ from .interfaces import (
30
+ ReportData,
31
+ ReporterConfig,
32
+ ReportFormatType,
33
+ ReportOutput,
34
+ ReportThemeType,
35
+ )
36
+
37
+ if TYPE_CHECKING:
38
+ from truthound_dashboard.db.models import Validation
39
+
40
+ logger = logging.getLogger(__name__)
41
+
42
+
43
+ class LegacyReportResult:
44
+ """Wrapper that mimics the old ReportResult interface.
45
+
46
+ This allows new reporters to be used with code that expects
47
+ the old ReportResult format.
48
+ """
49
+
50
+ def __init__(self, output: ReportOutput, config: ReporterConfig) -> None:
51
+ """Initialize from new ReportOutput.
52
+
53
+ Args:
54
+ output: New-style report output.
55
+ config: Reporter configuration used.
56
+ """
57
+ self._output = output
58
+ self._config = config
59
+
60
+ @property
61
+ def content(self) -> str | bytes:
62
+ return self._output.content
63
+
64
+ @property
65
+ def content_type(self) -> str:
66
+ return self._output.content_type
67
+
68
+ @property
69
+ def filename(self) -> str:
70
+ return self._output.filename
71
+
72
+ @property
73
+ def size_bytes(self) -> int:
74
+ return self._output.size_bytes
75
+
76
+ @property
77
+ def generation_time_ms(self) -> int:
78
+ return self._output.generation_time_ms
79
+
80
+ @property
81
+ def metadata(self) -> "LegacyReportMetadata":
82
+ return LegacyReportMetadata(self._output, self._config)
83
+
84
+
85
+ class LegacyReportMetadata:
86
+ """Wrapper that mimics the old ReportMetadata interface."""
87
+
88
+ def __init__(self, output: ReportOutput, config: ReporterConfig) -> None:
89
+ self._output = output
90
+ self._config = config
91
+
92
+ @property
93
+ def title(self) -> str:
94
+ return self._config.title
95
+
96
+ @property
97
+ def generated_at(self) -> Any:
98
+ from datetime import datetime
99
+
100
+ return datetime.utcnow()
101
+
102
+ @property
103
+ def source_name(self) -> str | None:
104
+ return self._output.metadata.get("source_name")
105
+
106
+ @property
107
+ def source_id(self) -> str | None:
108
+ return self._output.metadata.get("source_id")
109
+
110
+ @property
111
+ def validation_id(self) -> str | None:
112
+ return self._output.metadata.get("validation_id")
113
+
114
+ @property
115
+ def theme(self) -> Any:
116
+ """Return a theme object with .value property."""
117
+ from .base import ReportTheme
118
+
119
+ theme_str = self._config.theme.value
120
+ return ReportTheme(theme_str)
121
+
122
+ @property
123
+ def format(self) -> Any:
124
+ """Return a format object with .value property."""
125
+ from .base import ReportFormat
126
+
127
+ format_str = self._output.format.value
128
+ return ReportFormat(format_str)
129
+
130
+ def to_dict(self) -> dict[str, Any]:
131
+ return {
132
+ "title": self.title,
133
+ "generated_at": self.generated_at.isoformat(),
134
+ "source_name": self.source_name,
135
+ "source_id": self.source_id,
136
+ "validation_id": self.validation_id,
137
+ "theme": self._config.theme.value,
138
+ "format": self._output.format.value,
139
+ }
140
+
141
+
142
+ def convert_validation_to_report_data(validation: "Validation") -> ReportData:
143
+ """Convert a Validation model to ReportData.
144
+
145
+ This function bridges the old Validation model with the new
146
+ backend-agnostic ReportData format.
147
+
148
+ Args:
149
+ validation: Validation model from database.
150
+
151
+ Returns:
152
+ ReportData instance.
153
+ """
154
+ return ReportData.from_validation_model(validation)
155
+
156
+
157
+ def convert_theme(theme: str | Any) -> ReportThemeType:
158
+ """Convert theme string or old ReportTheme to new ReportThemeType.
159
+
160
+ Args:
161
+ theme: Theme as string or ReportTheme enum.
162
+
163
+ Returns:
164
+ ReportThemeType enum.
165
+ """
166
+ if isinstance(theme, ReportThemeType):
167
+ return theme
168
+ if hasattr(theme, "value"):
169
+ theme = theme.value
170
+ return ReportThemeType(theme)
171
+
172
+
173
+ def convert_format(format: str | Any) -> ReportFormatType:
174
+ """Convert format string or old ReportFormat to new ReportFormatType.
175
+
176
+ Args:
177
+ format: Format as string or ReportFormat enum.
178
+
179
+ Returns:
180
+ ReportFormatType enum.
181
+ """
182
+ if isinstance(format, ReportFormatType):
183
+ return format
184
+ if hasattr(format, "value"):
185
+ format = format.value
186
+ return ReportFormatType.from_string(format)
187
+
188
+
189
+ async def legacy_generate_report(
190
+ validation: "Validation",
191
+ *,
192
+ format: str = "html",
193
+ theme: str = "professional",
194
+ locale: str = "en",
195
+ title: str | None = None,
196
+ include_samples: bool = True,
197
+ include_statistics: bool = True,
198
+ custom_metadata: dict[str, Any] | None = None,
199
+ ) -> LegacyReportResult:
200
+ """Generate a report using the new infrastructure but returning old format.
201
+
202
+ This function provides backward compatibility with existing code
203
+ that uses the old generate_report signature.
204
+
205
+ Args:
206
+ validation: Validation model from database.
207
+ format: Report format (html, json, csv, etc.).
208
+ theme: Visual theme.
209
+ locale: Language locale.
210
+ title: Custom title.
211
+ include_samples: Include sample values.
212
+ include_statistics: Include statistics section.
213
+ custom_metadata: Additional metadata.
214
+
215
+ Returns:
216
+ LegacyReportResult that mimics the old ReportResult.
217
+ """
218
+ from .factory import generate_report_from_validation
219
+
220
+ # Build config
221
+ config = ReporterConfig(
222
+ title=title or f"Validation Report - {validation.source_id}",
223
+ theme=convert_theme(theme),
224
+ locale=locale,
225
+ include_samples=include_samples,
226
+ include_statistics=include_statistics,
227
+ custom_options=custom_metadata or {},
228
+ )
229
+
230
+ # Convert format
231
+ format_type = convert_format(format)
232
+
233
+ # Generate using new infrastructure
234
+ output = await generate_report_from_validation(
235
+ validation,
236
+ format=format_type,
237
+ config=config,
238
+ locale=locale,
239
+ )
240
+
241
+ return LegacyReportResult(output, config)
242
+
243
+
244
+ # Backward compatibility aliases
245
+ async def generate_report_compat(
246
+ validation: "Validation",
247
+ *,
248
+ format: str = "html",
249
+ theme: str = "professional",
250
+ locale: str = "en",
251
+ title: str | None = None,
252
+ include_samples: bool = True,
253
+ include_statistics: bool = True,
254
+ custom_metadata: dict[str, Any] | None = None,
255
+ ) -> LegacyReportResult:
256
+ """Alias for legacy_generate_report for explicit compatibility usage."""
257
+ return await legacy_generate_report(
258
+ validation,
259
+ format=format,
260
+ theme=theme,
261
+ locale=locale,
262
+ title=title,
263
+ include_samples=include_samples,
264
+ include_statistics=include_statistics,
265
+ custom_metadata=custom_metadata,
266
+ )
@@ -62,41 +62,8 @@ class CSVReporter(Reporter):
62
62
  output = io.StringIO()
63
63
  writer = csv.writer(output, delimiter=self._delimiter)
64
64
 
65
- # Write metadata section
66
- writer.writerow(["# Validation Report"])
67
- writer.writerow(["# Source", metadata.source_name or validation.source_id])
68
- writer.writerow(["# Validation ID", validation.id])
69
- writer.writerow(["# Generated At", metadata.generated_at.isoformat()])
70
- writer.writerow(
71
- [
72
- "# Status",
73
- "PASSED" if validation.passed else "FAILED",
74
- ]
75
- )
76
- writer.writerow([]) # Empty row separator
77
-
78
- # Write statistics section if requested
79
- if include_statistics:
80
- writer.writerow(["# Statistics"])
81
- writer.writerow(["Metric", "Value"])
82
- writer.writerow(["Row Count", validation.row_count or "N/A"])
83
- writer.writerow(["Column Count", validation.column_count or "N/A"])
84
- writer.writerow(["Total Issues", validation.total_issues or 0])
85
- writer.writerow(["Critical Issues", validation.critical_issues or 0])
86
- writer.writerow(["High Issues", validation.high_issues or 0])
87
- writer.writerow(["Medium Issues", validation.medium_issues or 0])
88
- writer.writerow(["Low Issues", validation.low_issues or 0])
89
- writer.writerow(
90
- [
91
- "Duration (ms)",
92
- validation.duration_ms if validation.duration_ms else "N/A",
93
- ]
94
- )
95
- writer.writerow([]) # Empty row separator
96
-
97
- # Write issues section
65
+ # Write issues section (main data)
98
66
  issues = self._extract_issues(validation)
99
- writer.writerow(["# Issues"])
100
67
 
101
68
  # Define headers based on options
102
69
  headers = ["Column", "Issue Type", "Severity", "Count", "Details"]
@@ -116,7 +83,7 @@ class CSVReporter(Reporter):
116
83
  issue.get("details", "") or "",
117
84
  ]
118
85
  if include_samples:
119
- samples = issue.get("sample_values", [])
86
+ samples = issue.get("sample_values") or []
120
87
  samples_str = "; ".join(str(v)[:50] for v in samples[:5])
121
88
  row.append(samples_str)
122
89