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
|
@@ -379,6 +379,7 @@ class ValidationService:
|
|
|
379
379
|
"""Service for running and managing validations.
|
|
380
380
|
|
|
381
381
|
Handles validation execution, result storage, and history.
|
|
382
|
+
Supports both built-in truthound validators and custom validators.
|
|
382
383
|
"""
|
|
383
384
|
|
|
384
385
|
def __init__(self, session: AsyncSession) -> None:
|
|
@@ -399,6 +400,7 @@ class ValidationService:
|
|
|
399
400
|
*,
|
|
400
401
|
validators: list[str] | None = None,
|
|
401
402
|
validator_params: dict[str, dict[str, Any]] | None = None,
|
|
403
|
+
custom_validators: list[dict[str, Any]] | None = None,
|
|
402
404
|
schema_path: str | None = None,
|
|
403
405
|
auto_schema: bool = False,
|
|
404
406
|
columns: list[str] | None = None,
|
|
@@ -411,7 +413,8 @@ class ValidationService:
|
|
|
411
413
|
"""Run validation on a source.
|
|
412
414
|
|
|
413
415
|
This method provides full access to truthound's th.check() parameters,
|
|
414
|
-
allowing fine-grained control over validation behavior.
|
|
416
|
+
allowing fine-grained control over validation behavior. It also supports
|
|
417
|
+
running custom validators alongside built-in validators.
|
|
415
418
|
|
|
416
419
|
Args:
|
|
417
420
|
source_id: Source ID to validate.
|
|
@@ -420,6 +423,8 @@ class ValidationService:
|
|
|
420
423
|
Format: {"ValidatorName": {"param1": value1, "param2": value2}}
|
|
421
424
|
Example: {"Null": {"columns": ["email"], "mostly": 0.95},
|
|
422
425
|
"CompletenessRatio": {"column": "phone", "min_ratio": 0.98}}
|
|
426
|
+
custom_validators: Optional list of custom validator configs.
|
|
427
|
+
Format: [{"validator_id": "...", "column": "...", "params": {...}}]
|
|
423
428
|
schema_path: Optional schema file path.
|
|
424
429
|
auto_schema: Auto-learn schema if True.
|
|
425
430
|
columns: Columns to validate. If None, validates all columns.
|
|
@@ -448,7 +453,7 @@ class ValidationService:
|
|
|
448
453
|
)
|
|
449
454
|
|
|
450
455
|
try:
|
|
451
|
-
# Run validation with all supported parameters
|
|
456
|
+
# Run built-in validation with all supported parameters
|
|
452
457
|
result = await self.adapter.check(
|
|
453
458
|
source.source_path or "",
|
|
454
459
|
validators=validators,
|
|
@@ -463,8 +468,19 @@ class ValidationService:
|
|
|
463
468
|
pushdown=pushdown,
|
|
464
469
|
)
|
|
465
470
|
|
|
466
|
-
#
|
|
467
|
-
|
|
471
|
+
# Run custom validators if specified
|
|
472
|
+
custom_results = []
|
|
473
|
+
if custom_validators:
|
|
474
|
+
custom_results = await self._run_custom_validators(
|
|
475
|
+
source=source,
|
|
476
|
+
custom_validators=custom_validators,
|
|
477
|
+
validation_id=str(validation.id),
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
# Update validation with combined results
|
|
481
|
+
await self._update_validation_success(
|
|
482
|
+
validation, result, custom_results=custom_results
|
|
483
|
+
)
|
|
468
484
|
|
|
469
485
|
# Update source last validated
|
|
470
486
|
source.last_validated_at = datetime.utcnow()
|
|
@@ -477,29 +493,156 @@ class ValidationService:
|
|
|
477
493
|
await self.session.refresh(validation)
|
|
478
494
|
return validation
|
|
479
495
|
|
|
496
|
+
async def _run_custom_validators(
|
|
497
|
+
self,
|
|
498
|
+
source: Source,
|
|
499
|
+
custom_validators: list[dict[str, Any]],
|
|
500
|
+
validation_id: str,
|
|
501
|
+
) -> list[dict[str, Any]]:
|
|
502
|
+
"""Run custom validators on source data.
|
|
503
|
+
|
|
504
|
+
Args:
|
|
505
|
+
source: Data source to validate.
|
|
506
|
+
custom_validators: List of custom validator configs.
|
|
507
|
+
validation_id: Parent validation ID.
|
|
508
|
+
|
|
509
|
+
Returns:
|
|
510
|
+
List of custom validator results.
|
|
511
|
+
"""
|
|
512
|
+
from truthound_dashboard.core.plugins import CustomValidatorExecutor
|
|
513
|
+
from truthound_dashboard.core.plugins.registry import plugin_registry
|
|
514
|
+
from truthound_dashboard.core.plugins.validator_executor import ValidatorContext
|
|
515
|
+
|
|
516
|
+
results = []
|
|
517
|
+
executor = CustomValidatorExecutor()
|
|
518
|
+
|
|
519
|
+
# Load source data once
|
|
520
|
+
try:
|
|
521
|
+
import polars as pl
|
|
522
|
+
|
|
523
|
+
source_path = source.source_path or ""
|
|
524
|
+
if source.type == "csv":
|
|
525
|
+
df = pl.read_csv(source_path)
|
|
526
|
+
elif source.type == "parquet":
|
|
527
|
+
df = pl.read_parquet(source_path)
|
|
528
|
+
elif source.type == "json":
|
|
529
|
+
df = pl.read_json(source_path)
|
|
530
|
+
else:
|
|
531
|
+
# Unsupported source type for custom validators
|
|
532
|
+
return results
|
|
533
|
+
except Exception:
|
|
534
|
+
return results
|
|
535
|
+
|
|
536
|
+
for cv_config in custom_validators:
|
|
537
|
+
validator_id = cv_config.get("validator_id")
|
|
538
|
+
column_name = cv_config.get("column")
|
|
539
|
+
params = cv_config.get("params", {})
|
|
540
|
+
|
|
541
|
+
if not validator_id or not column_name:
|
|
542
|
+
continue
|
|
543
|
+
|
|
544
|
+
# Get the custom validator
|
|
545
|
+
validator = await plugin_registry.get_validator(
|
|
546
|
+
self.session, validator_id=validator_id
|
|
547
|
+
)
|
|
548
|
+
if not validator or not validator.is_enabled:
|
|
549
|
+
continue
|
|
550
|
+
|
|
551
|
+
# Check column exists
|
|
552
|
+
if column_name not in df.columns:
|
|
553
|
+
results.append({
|
|
554
|
+
"validator_id": validator_id,
|
|
555
|
+
"validator_name": validator.name,
|
|
556
|
+
"column": column_name,
|
|
557
|
+
"passed": False,
|
|
558
|
+
"error": f"Column '{column_name}' not found",
|
|
559
|
+
})
|
|
560
|
+
continue
|
|
561
|
+
|
|
562
|
+
# Create context
|
|
563
|
+
column_values = df[column_name].to_list()
|
|
564
|
+
context = ValidatorContext(
|
|
565
|
+
column_name=column_name,
|
|
566
|
+
column_values=column_values,
|
|
567
|
+
parameters=params,
|
|
568
|
+
schema={"dtype": str(df[column_name].dtype)},
|
|
569
|
+
row_count=len(column_values),
|
|
570
|
+
)
|
|
571
|
+
|
|
572
|
+
# Execute
|
|
573
|
+
try:
|
|
574
|
+
result = await executor.execute(
|
|
575
|
+
validator=validator,
|
|
576
|
+
context=context,
|
|
577
|
+
session=self.session,
|
|
578
|
+
source_id=str(source.id),
|
|
579
|
+
)
|
|
580
|
+
results.append({
|
|
581
|
+
"validator_id": validator_id,
|
|
582
|
+
"validator_name": validator.name,
|
|
583
|
+
"column": column_name,
|
|
584
|
+
"passed": result.passed,
|
|
585
|
+
"issues": result.issues,
|
|
586
|
+
"message": result.message,
|
|
587
|
+
"execution_time_ms": result.execution_time_ms,
|
|
588
|
+
})
|
|
589
|
+
except Exception as e:
|
|
590
|
+
results.append({
|
|
591
|
+
"validator_id": validator_id,
|
|
592
|
+
"validator_name": validator.name,
|
|
593
|
+
"column": column_name,
|
|
594
|
+
"passed": False,
|
|
595
|
+
"error": str(e),
|
|
596
|
+
})
|
|
597
|
+
|
|
598
|
+
return results
|
|
599
|
+
|
|
480
600
|
async def _update_validation_success(
|
|
481
601
|
self,
|
|
482
602
|
validation: Validation,
|
|
483
603
|
result: CheckResult,
|
|
604
|
+
custom_results: list[dict[str, Any]] | None = None,
|
|
484
605
|
) -> None:
|
|
485
606
|
"""Update validation with successful result.
|
|
486
607
|
|
|
487
608
|
Args:
|
|
488
609
|
validation: Validation record to update.
|
|
489
|
-
result: Check result from
|
|
610
|
+
result: Check result from built-in validators.
|
|
611
|
+
custom_results: Optional results from custom validators.
|
|
490
612
|
"""
|
|
491
|
-
|
|
492
|
-
|
|
613
|
+
# Calculate combined pass/fail status
|
|
614
|
+
builtin_passed = result.passed
|
|
615
|
+
custom_passed = True
|
|
616
|
+
custom_issues_count = 0
|
|
617
|
+
|
|
618
|
+
if custom_results:
|
|
619
|
+
for cr in custom_results:
|
|
620
|
+
if not cr.get("passed", True):
|
|
621
|
+
custom_passed = False
|
|
622
|
+
custom_issues_count += len(cr.get("issues", []))
|
|
623
|
+
|
|
624
|
+
combined_passed = builtin_passed and custom_passed
|
|
625
|
+
|
|
626
|
+
validation.status = "success" if combined_passed else "failed"
|
|
627
|
+
validation.passed = combined_passed
|
|
493
628
|
validation.has_critical = result.has_critical
|
|
494
629
|
validation.has_high = result.has_high
|
|
495
|
-
validation.total_issues = result.total_issues
|
|
630
|
+
validation.total_issues = result.total_issues + custom_issues_count
|
|
496
631
|
validation.critical_issues = result.critical_issues
|
|
497
632
|
validation.high_issues = result.high_issues
|
|
498
633
|
validation.medium_issues = result.medium_issues
|
|
499
634
|
validation.low_issues = result.low_issues
|
|
500
635
|
validation.row_count = result.row_count
|
|
501
636
|
validation.column_count = result.column_count
|
|
502
|
-
|
|
637
|
+
|
|
638
|
+
# Combine results
|
|
639
|
+
combined_result = result.to_dict()
|
|
640
|
+
if custom_results:
|
|
641
|
+
combined_result["custom_validators"] = custom_results
|
|
642
|
+
combined_result["custom_validators_passed"] = custom_passed
|
|
643
|
+
combined_result["custom_issues_count"] = custom_issues_count
|
|
644
|
+
|
|
645
|
+
validation.result_json = combined_result
|
|
503
646
|
validation.completed_at = datetime.utcnow()
|
|
504
647
|
|
|
505
648
|
if validation.started_at:
|